这是我正在开发的一个简单表单库的摘录。表单具有初始值和当前值。当前值可以重置为初始值。可选,重置后也可以更改初始值:
const useForm = ((initialInitialValue: T) => {
// Uses state since the initial value can be changed by reset:
const [initialValue, setInitialValue] = useState(initialInitialValue);
const [value, setValue] = useState(initialInitialValue);
const reset = useCallback((newInitialValue?: T) => {
if (newInitialValue !== undefined) {
setInitialValue(newInitialValue);
}
setValue(newInitialValue ?? initialValue);
}, [initialValue]);
return {reset, setValue, value};
};
不幸的是,使用
reset
函数很容易创建无限循环:
// Wait until myInitialValue is available (e.g. from the react-query's useQuery):
useEffect(() => {
if (myInitialValue === undefined) {
return;
}
reset(myInitialValue);
}, [myInitialValue, reset]);
由于
reset
改变了钩子的initialValue
,reset
将成为一个新函数,它将再次触发该效果,无限循环。
人们可以想象一些 hacky 解决方法,例如将
useRef
与 useEffect
一起使用,仅调用 reset
一次(如果这在您的应用程序中合适),但这非常脆弱,因为 useForm
自定义挂钩的每个用户都必须这样做(并且必须意识到他们需要这样做)。换句话说,上面写的useForm
是一个糟糕的、有漏洞的抽象。
在 React 的未来版本中,我们可以使用
useEffectEvent
来实现上述 useRef
hack 的稍微好一点的版本。然而,
useEffectEvent
今天不可用并且建议怎样写
useForm
才能不具有此属性。
利用useRef来存储初始值和重置逻辑。这可以防止依赖项导致不必要的重新渲染。
const resetRef = useRef();
// Store the reset function in the ref to prevent re-creation
useEffect(() => {
resetRef.current = reset;
}, [reset]);
更改退货声明
return { reset: resetRef.current, setValue, value };
const MyFormComponent = ({ myInitialValue }) => {
const { reset, setValue, value } = useForm(myInitialValue);
useEffect(() => {
useEffect(() => {
if (myInitialValue === undefined) {
if (myInitialValue !== undefined) {
return;
}
reset(myInitialValue);
reset(myInitialValue);
}
}, [myInitialValue, reset]);
}, [myInitialValue, reset]);
return (
<div>
<input value={value} onChange={(e) => setValue(e.target.value)} />
<button onClick={() => reset()}>Reset</button>
</div>
);
};