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