React hook的另类成道
前景
众所周知大道三千,主流的React hook道法是 useCallback(函数)+useEffect(watch)+useRef(不监听的变量)+useState(状态独立管理)+useMemo(计算属性/jsx)。除了主流之外还有很多用法,今日就来说说一种你看了一定会喊666的道法
道法说明
事先声明:本道法经过业务的验证,在复杂业务中使用至今没啥问题,若道友们不甚走火入魔,概不负责🤪
进入正题,利用useRef的穿透特性,跳过函数的依赖编写过程,让开发者更加关注函数的逻辑实现,而不是分散一部分注意力在依赖上(即使vscode可以自动帮你补全),先直接上demo,然后再说下利弊
- use-immer 谁用谁知道,这可是官网推荐的哦
- antd ui库
import React, { useRef } from 'react'
import { useImmer } from 'use-immer'
import { Input, Select } from 'antd'
export default function Demo(props) {
const [state, setState] = useImmer({
formData: {
type: 1,
labelList: ['new', 'old', 'pre'],
},
})
const { formData } = state
const stateRef = useRef(null)
stateRef.current = { ...state }
const fnsRef = useRef({
init() {
setState((state) => {
state.formData = initFormData()
})
},
updateLabel(index, label) {
const {
formData: { type },
} = stateRef.current
if (type !== 1) {
setState((state) => {
state.labelList[index] = label
})
}
},
})
const { updateLabel } = fnsRef.current
useEffect(() => {
fnsRef.current.init()
}, [])
return (
<div>
<Select
value={formData.type}
options={[
{ value: 1, label: '不可变' },
{ value: 2, label: '可变' },
]}
/>
{formData.labelList.map((label, index) => (
<Input
value={label}
key={index}
onChange={(e) => updateLabel(e.target.value, index)}
/>
))}
</div>
)
}
看完demo,我想诸位应该有众多疑惑,接下来依依解答
- 使用use-immer代替useState,更新状态变得简单,而且渲染性能更优,useState看似把状态的变化都隔离开来,方便追踪,但是实际使用时候,一句debugger就完了。特别是复杂的状态更新,你可能要写一堆{...state}类似语句。
- 使用ref来定义函数代替useCallback,函数永远不存在像useCallback因为依赖变化而更新的情况,对渲染友好,写函数的时候,再也不用写一堆const xxx = useCallback(cb, deps)了,因为stateRef把最新的state存了下来,直接获取就行,不需要考虑依赖。个人觉得,函数就应该考虑逻辑的实现,而不是把许多精力放在依赖上面,这样写函数才会回归我们的简单存粹
- 正常来说,我们使用useEffect只是希望监听状态发生变化了才会执行某一个函数,但是呢因为hook里把函数useCallback也当成可一个变量来看待,所以还需要在依赖里加上这个函数变量,而这个函数可能又依赖于其他的我们不关心的变量,这就变成了我只想监听变量a、b变化时候,执行fn,而fn依赖于c、d,变相的整个依赖变成了四个。为了防止这种现象发生可能会直接禁止eslint去检查这一个useEffect。所以换一种写法,ref让我们跳过不想要的监听,让我们写代码时候更加专注于逻辑的实现
总结
总的来说,好处挺多,这种写法最不爽的地方就在于重复的写stateRef赋值和结构变量。没有完美的写法,只有合适的道法,诸位道友你心动了么🤔
转载自:https://juejin.cn/post/7237840998986301498