受控模式
- 数据在组件外
- 能通过业务代码改变数据
function App() {
const [value, setValue] = useState('guang');
function onChange(event: ChangeEvent<HTMLInputElement>) {
setValue(event.target.value);
}
return <input value={value} onChange={onChange}/>
}
非受控模式
- 数据在组件内
- 不能通过业务代码改变数据
function App() {
function onChange(event: ChangeEvent<HTMLInputElement>) {
console.log(event.target.value);
}
return <input defaultValue={'guang'} onChange={onChange}/>
}
useControllableValue
对组件数据的封装,兼容了受控模式与非受控模式,来自ahooks
function useControllableValue(defaultProps, options = {}) {
const props = defaultProps ?? {};
const {
defaultValue, // 既没有 value 也没有 defaultValue 时使用的值
defaultValuePropName = 'defaultValue',
valuePropName = 'value',
trigger = 'onChange',
} = options;
const value = props[valuePropName];
// 根据是否有 value 判断是否为受控模式
const isControlled = Object.prototype.hasOwnProperty.call(props, valuePropName);
// 组件初始值,后续不会改变
const initialValue = useMemo(() => {
if (isControlled) {
return value;
}
if (Object.prototype.hasOwnProperty.call(props, defaultValuePropName)) {
return props[defaultValuePropName];
}
return defaultValue;
}, []);
// 组件状态,受控模式/非受控模式共用
const stateRef = useRef(initialValue);
if (isControlled) {
stateRef.current = value;
}
// 强制组件更新的 hook
const update = useUpdate();
function setState(v: SetStateAction<T>, ...args: any[]) {
const r = isFunction(v) ? v(stateRef.current) : v;
if (!isControlled) {
stateRef.current = r;
update();
}
if (props[trigger]) {
props[trigger](r, ...args);
}
}
return [stateRef.current, useMemoizedFn(setState)] as const;
}
interface compProps {
value?: string,
defaultValue?: string,
onChange?: (val: string) => void
}
function BizComp(props: any) {
const [state, setState] = useControllableValue<string>(props);
return <input value={state} onChange={(e) => setState(e.target.value)} />;
};
function App() {
const [val, setVal] = useState('');
return <BizComp value={val} onChange={(val) => {
setVal(val);
}}></BizComp>
}