Open kangyana opened 2 years ago
无论哪种模式都使用 子组件状态 。 当 受控模式 时,将 父组件状态 同步给 子组件状态。
const Input = (props) => {
const { value: _value, onChange } = props;
const [value, setValue] = useState(_value);
const isControlled =_value !== undefined; // 是否受控
const handleChange = (e) => {
// 不受控才改变内部状态
if (!isControlled) setValue(e.target.value);
onChange(e.target.value);
}
useEffect(() => {
// 外部值变化,手动同步内部状态
if (isControlled) setValue(_value);
}, [isControlled])
return <input value={value} onChange={handleChange} />
}
仔细看上面的代码,我们会发现在 受控模式 下存在两个问题:
useEffect
使用 setState
,会额外触发一次渲染。我们不需要 子组件状态 和 父组件状态 时刻统一。 只需要判断,受控模式 下直接使用 父组件状态 就好了。
这样即使状态的同步存在延迟,子组件使用的值也是最新的。
const Input = (props) => {
// ...同上面的
const finalValue = isControlled ? _value : value; // 真正使用的值
return <input value={finalValue} onChange={handleChange} />
}
在 useEffect
使用 setState
,会额外触发一次 子组件 的渲染。
state
的作用是什么?state
机制?可以使用 ref
+ forceUpdate
的组合。
图中的虚线浅色圆圈表示 ref
,刷新图标表示 forceUpdate
函数。
const Input = (props) => {
const { value: _value, onChange } = props;
const [flag, forceUpdate] = useReducer(v => v + 1, 0); // 触发渲染用的
const isControlled =_value !== undefined; // 是否受控
const stateRef = useRef(_value);
// 受控模式下,将外部的值同步给 Ref
if (isControlled) {
stateRef.current = _value;
}
const handleChange = (e) => {
// 手动同步 Ref
stateRef.current = e.target.value;
// 手动触发渲染
forceUpdate();
onChange(e.target.value);
}
return <input value={stateRef.current} onChange={handleChange} />
}
我们可以把 受控又非受控组件
的效果封装为 Hook
,供其他表单组件使用。
const usePropsValue = (props) => {
const { value, onChange, defaultValue } = props;
const [flag, forceUpdate] = useReducer(v => v + 1, 0); // 触发渲染用的
const isControlled = useMemo(() => value !== undefined, [value]); // 是否受控
const stateRef = useRef(isControlled ? value : defaultValue);
// 受控模式下,将外部的值同步给 Ref
if (isControlled) {
stateRef.current = value;
}
const setState = (nextValue) => {
if (nextValue === stateRef.current) return;
// 手动同步 Ref
stateRef.current = nextValue;
// 手动触发渲染
forceUpdate();
onChange?.(nextValue);
}
return [stateRef.current, setState];
}
1. 受控组件
行为受 React 状态(state) 控制的表单组件。
2. 非受控组件
行为不受 React 状态 控制的表单组件,行为完全由用户操作影响。
3. 受控组件 和 非受控组件 的区别
控制组件行为的 状态 是否由外部传入。
图中蓝色表示 组件,黄色表示 状态。
4. 受控又非受控组件
现代组件库,有非常多的组件需要做到既支持受控模式,又支持非受控模式。 例如
antd
涉及 输入值、切换、展开收起 的组件,都需要做到。