功能
在某些组件开发时,我们需要组件的状态即可以自己管理,也可以被外部控制,useControllableValue 就是帮你管理这种状态的 Hook。
解析
实现的思路
- 接受组件的参数
- 如果在组件的props中,存在名为[valuePropName]的属性,发生变化时直接变更对应的属性
- 如果在组件的props中,存在名为[trigger]的时间,则调用handleSetState方法时,直接触发对应的[trigger]方法
我学习到的点
- 利用变量来储存动态的名称,在函数内部保持了逻辑的相对稳定。
- 内部监听value,valuePropName的时候,其实不需要init时候的状态。所以用useUpdateEffect 比较合理
- value的值不确定,这里可以在自定义hook上定义范型T来支持。
使用
出参
| 参数 | 说明 | 类型 |
|---|
| state | 状态值 | - |
| setState | 修改 state 的函数 | (value: any) => void |
入参
| 参数 | 说明 | 类型 | 默认值 |
|---|
| props | 组件的 props | object | - |
| options | 可选配置项,见 Options | - | - |
options
| 参数 | 说明 | 类型 | 默认值 |
|---|
| defaultValue | 默认值,会被 props.defaultValue 和 props.value 覆盖 | - | - |
| defaultValuePropName | 默认值的属性名 | string | defaultValue |
| valuePropName | 值的属性名 | string | value |
| trigger | 修改值时,触发的函数 | string | onChange |
源码
import { useCallback, useState, useEffect, useRef } from "react"
import useUpdateEffect from "../useUpdateEffect"
export interface Options<T> {
defaultValue?: T
defaultValuePropName?: string
valuePropName?: string
trigger?: string
}
export interface Props {
[key: string]: any
}
export default function useControllableValue<T>(
props: Props = {},
options: Options<T> = {}
) {
const {
defaultValue,
defaultValuePropName = "defaultValue",
valuePropName = "value",
trigger = "onChange",
} = options
const value = props[valuePropName]
const [state, setState] = useState<T | undefined>(() => {
if (valuePropName in props) {
return value
}
if (defaultValuePropName in props) {
return props[defaultValuePropName]
}
return defaultValue
})
/* init 的时候不用执行了 */
useUpdateEffect(() => {
if (valuePropName in props) {
setState(value)
}
}, [value, valuePropName])
const handleSetState = useCallback(
(v: T | undefined) => {
if (!(valuePropName in props)) {
setState(v)
}
if (props[trigger]) {
props[trigger](v)
}
},
[props, valuePropName, trigger]
)
return [state, handleSetState] as const
}