热门组件库都在使用的usePropsValue怎么写
点击关注公众号,“技术干货” 及时达!
1、受控和非受控
❝大家可能经常会封装一些组件,纯UI组件也好,业务组件也好,但是不能脱离受控非受控的概念去写组件,这样很容易写出渲染次数过多的组件
❞
1.1、受控
受控意味着「受代码控制」,例如一个Input的修改是你使用onChange将他的value修改为e.target.value
1.2、非受控
非受控意味着「代码无法改变组件的值」,组件往往只需要一个defaultValue,比如Input你可以不需要监听onChange,而是直接用form得到每个表单的输入,最后提交,所有组件的值是用户手动改变的,在react中往往体现为使用ref去拿到组件的引用。
1.3、理解这个对写组件有什么帮助?
你是否一股脑的接受用户的传入参数并手动set修改一次? 实际对于某些组件,往往我们不需要手动修改组件的值,手动的set往往导致多次渲染 例如表单,如果你只是需要表单的值,你没必要每个字段给一个value然后修改的时候set它 同理还有一些组件例如开关,日历,在我们不需要控制组件的值情况下,我们直接使用非受控模式即可,减少代码编写量的同时,还减少了渲染次数
1.4、什么是usePropsValue?
2、usePropsValue编写思路
我们希望使用usePropsValue以后就和正常使用useState一样,但是这个setState的操作逻辑在usePropsValue中,我们在代码中只需要 const [selfValue,setSelfValue] = usePropsValue({
value: props.value,
defaultValue: props.defaultValue,
onChange: props.onChange
})入参只有defaultValue的时候,使用useRef修改current 入参数有value和onChange的时候要将onChange作为setState返回并修改value,这里要注意onChange
3、编写一个简单的usePropsValue
因为为了所有组件通用,入参用泛型 useRef判断是defaultValue还是value 这里要注意因为非受控用的是useRef不会触发组件的更新渲染,所以这里我们使用ahooks更新 同理也可以自己写一个state去更新渲染状态 import { useUpdate } from 'ahooks'
import { SetStateAction, useRef } from 'react'type Options<T> = {
value?: T
defaultValue: T
onChange?: (v: T) => void
}
export const usePropsValue = <T>(options: Options<T>) => {
const { value, defaultValue, onChange } = optionsconst stateRef = useRef<T>(value !== undefined ? value : defaultValue)
if (value !== undefined) {
stateRef.current = value
}const update = useUpdate()
// setState用户传入的可能是函数,需要判断当是函数的时候直接更新为函数return的值
const setState = (v: SetStateAction<T>) => {
const nextValue =
typeof v === 'function' ? (v as (preState: T) => T)(stateRef.current) : v
stateRef.current = nextValue
update()
return onChange?.(nextValue)
}return [stateRef.current,setState] as const
}
4、优化方案
因为同步写法会直接更新stateRef,所以当value变化的时候要对比一下, if (nextValue === stateRef.current) return使用 useCallBack或者useMemoizedFn包裹setState
「避免不必要的重新渲染」:如果 setState函数在每次渲染时都被重新创建,那么它的引用就会发生变化,可能会导致依赖它的子组件或useEffect钩子重新执行,这可能会引发不必要的重新渲染或副作用。而使用useMemoizedFn可以确保函数的引用是稳定的,除非其依赖项发生变化。「性能优化」:在某些场景下,函数可能会被频繁调用。如果每次都重新创建函数对象可能会增加内存和性能开销。使用 useMemoizedFn可以减少这类开销。「稳定的回调函数」:在某些情况下,组件可能会传递 setState给子组件或其他钩子。如果这些子组件或钩子依赖于稳定的回调函数,那么使用useMemoizedFn就可以确保它们收到的始终是同一个函数实例,避免因为函数引用变化而导致的不必要更新。
5、结合组件实现
在代码仓库实现了一个Switch组件并使用usePropsValue 有兴趣可以看看https://github.com/Youngzx88/article-demo/tree/main/use-props-value
6、总结
其实多看看这种开源的组件库,会发现有很多类似的hooks封装,这都是大家互相 借鉴+创新,通过优秀的设计模式实现的多了解了解这种hooks有助于写出高可维护的组件,像usePropsValue就可以让我们无脑去使用,不需要管组件本身是受控还是非受控,全看你的入参是什么
点击关注公众号,“技术干货” 及时达!