【ahooks源码解析】useControllableValue

2,403 阅读1分钟

功能

在某些组件开发时,我们需要组件的状态即可以自己管理,也可以被外部控制,useControllableValue 就是帮你管理这种状态的 Hook。

解析

实现的思路

  • 接受组件的参数
  • 如果在组件的props中,存在名为[valuePropName]的属性,发生变化时直接变更对应的属性
  • 如果在组件的props中,存在名为[trigger]的时间,则调用handleSetState方法时,直接触发对应的[trigger]方法

我学习到的点

  • 利用变量来储存动态的名称,在函数内部保持了逻辑的相对稳定。
  • 内部监听value,valuePropName的时候,其实不需要init时候的状态。所以用useUpdateEffect 比较合理
  • value的值不确定,这里可以在自定义hook上定义范型T来支持。

使用

出参

参数说明类型
state状态值-
setState修改 state 的函数(value: any) => void

入参

参数说明类型默认值
props组件的 propsobject-
options可选配置项,见 Options--

options

参数说明类型默认值
defaultValue默认值,会被 props.defaultValue 和 props.value 覆盖--
defaultValuePropName默认值的属性名stringdefaultValue
valuePropName值的属性名stringvalue
trigger修改值时,触发的函数stringonChange

源码

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;
}