advanced 性能相关

44 阅读1分钟

useControllableValue

组件的状态既可以自己管理,也可以被外部控制

import { useMemo, useRef } from 'react';
import useMemoizedFn from '../useMemoizedFn';
import useUpdate from '../useUpdate';

export interface Options<T> {
  defaultValue?: T;
  defaultValuePropName?: string;
  valuePropName?: string;
  trigger?: string;
}

export type Props = Record<string, any>;

export interface StandardProps<T> {
  value: T;
  defaultValue?: T;
  onChange: (val: T) => void;
}

function useControllableValue<T = any>(
  props: Props = {},
  options: Options<T> = {},
) {
  const {
    defaultValue,
    defaultValuePropName = 'defaultValue',
    valuePropName = 'value',
    trigger = 'onChange',
  } = options;

  const value = props[valuePropName] as T;
  const isControlled = valuePropName in props;

  const initialValue = useMemo(() => {
    // 受控:使用 props.value
    if (isControlled) {
      return value;
    }
    // 使用 props.defaultValue
    if (defaultValuePropName in props) {
      return props[defaultValuePropName];
    }
    return defaultValue;
  }, []);

  const stateRef = useRef(initialValue);
  if (isControlled) {
    stateRef.current = value;
  }

  const update = useUpdate();

  const setState = (v: T, ...args: any[]) => {
    const r = isControlled ? props[trigger] : setStateRef;
    
    // 受控:调用 props.onChange,非受控:更新内部状态
    if (typeof r === 'function') {
      r(v, ...args);
    }
    
    // 非受控模式下触发重新渲染
    if (!isControlled) {
      update();
    }
  };

  const setStateRef = useMemoizedFn((v: T, ...args: any[]) => {
    const newValue = typeof v === 'function' ? v(stateRef.current, ...args) : v;
    stateRef.current = newValue;
  });

  return [stateRef.current, useMemoizedFn(setState)] as const;
}

export default useControllableValue;

function CustomInput(props) {
  const [value, onChange] = useControllableValue(props);
  
  return (
    <input
      value={value}
      onChange={(e) => onChange(e.target.value)}
    />
  );
}

// 受控使用
<CustomInput value=" controlled" onChange={(val) => console.log(val)} />

// 非受控使用
<CustomInput defaultValue="uncontrolled" />

useCreation

useCreation 是 useMemo 或 useRef 的替代品

useEventEmitter

在多个组件之间进行事件通知

useLatest

返回当前最新值的 Hook,可以避免闭包问题

import { useRef } from 'react';

function useLatest<T>(value: T) {
  const ref = useRef(value);
  ref.current = value;

  return ref;
}

export default useLatest;

useMemoizedFn

持久化 function 的 Hook,一般情况下,可以使用 useMemoizedFn 完全代替 useCallback

useReactive

提供一种数据响应式的操作体验,定义数据状态不需要写useState