React中useState结合localStorage存储表单输入值无法加载问题排查
问题根本原因
你原来的代码存在几个核心错误导致加载失效:
- 你的
useInput是独立调用两次的(姓名、手机号各调用一次),每个useInput实例内部的enteredValue是独立的状态,但你原来读取localStorage的时候,把姓名、手机号两个值同时塞到了每个独立的useInput状态里,而且你的输入框绑定的是enteredValue.enterValidate字段,你塞的fullNameEntered、phoneEntered根本没有被用到,当然显示不出来。 - 原来的存储逻辑是在父组件统一把两个输入值存到同一个
datakey里,但是两个独立的useInput实例不知道自己对应的是fullName还是phone字段,也没办法正确匹配取值。
优化方案(比你当前的实现更通用、易维护)
1. 改造useInput,增加入参标识当前输入框的存储key
const useInput = (validateValue, storageKey) => { // 初始化直接从localStorage读对应key的值,不需要读所有字段 const [enteredValue, setEnteredValue] = useState(() => { const saved = localStorage.getItem(storageKey); return { enterValidate: saved ? JSON.parse(saved) : "", }; }); const [isTouched, setIsTouched] = useState(false); const [clickClasses, setClickClasses] = useState(false); const valueIsValid = validateValue(enteredValue.enterValidate); const hasError = !valueIsValid && isTouched; const valueChangeHandler = (event) => { const val = event.target.value; setEnteredValue({ ...enteredValue, enterValidate: val, }); // 输入时直接存对应key的值 localStorage.setItem(storageKey, JSON.stringify(val)); }; // 剩下的原有逻辑不变,blur、click、reset方法都保留 const inputBlurHandler = () => { setIsTouched(true); setClickClasses(false); }; const inputClickHandler = () => { setClickClasses(!clickClasses); }; const reset = () => { setEnteredValue({ enterValidate: "" }); setIsTouched(false); localStorage.removeItem(storageKey); }; return { value: enteredValue.enterValidate, isValid: valueIsValid, hasError, valueChangeHandler, inputBlurHandler, inputClickHandler, click: clickClasses, reset, }; };
2. 父组件调用时传入对应存储key,删除统一存localStorage的逻辑
// 姓名输入框调用,传入存储key为fullName const { value: fullNameEntered, isValid: enteredFullNameIsValid, hasError: fullNameHasError, valueChangeHandler: fullNameChangeHandler, inputBlurHandler: fullNameBlurHandler, inputClickHandler: fullNameClickHandler, click: fullNameClickClasses, } = useInput((value) => { const inputValidFullName = { value: value, maxLength: 20, whiteSpace: true, allowNumber: false, allowStrings: true, }; return validateInput(inputValidFullName); }, "fullName"); // 手机号输入框调用,传入存储key为phoneNumber const { value: phoneEntered, isValid: enteredPhoneIsValid, hasError: phoneHasError, valueChangeHandler: phoneChangeHandler, inputBlurHandler: phoneBlurHandler, } = useInput((value) => { const inputValidPhone = { value: value, maxLength: 15, whiteSpace: true, allowNumber: true, allowStrings: false, }; return validateInput(inputValidPhone); }, "phoneNumber"); // 原来的useEffect里删除localStorage.setItem("data")的逻辑,只保留setValidation的逻辑 useEffect(() => { let validateInputs = { fullNameValidate: enteredFullNameIsValid, phoneValidate: enteredPhoneIsValid, }; setValidation(validateInputs); }, [enteredFullNameIsValid, enteredPhoneIsValid, setValidation]);
方案优势
- 每个
useInput实例独立管理自己的存储逻辑,不需要提前硬编码所有输入框的字段,后续新增输入框只需要传入对应的存储key即可,扩展性更强 - 状态和存储一一对应,不会出现字段混乱的问题
- 初始化直接通过useState的初始化函数读值,比useEffect读值更早,不会出现闪屏
内容的提问来源于stack exchange,提问作者Drew94
相关产品推荐
相关产品推荐

