React中useState setter在函数内无法实时反射值的问题求助
React表单验证首次提交状态判断异常问题
问题描述
在函数中同时读取和设置React useState状态值做表单验证,点击提交按钮触发onSubmit函数时,首次点击即便存在验证错误,仍会判定表单有效,第二次点击才能得到正确结果。
代码示例
const [bookingToDateError, setBookingToDateError] = useState<string | undefined>(); const [bookingFromDateError, setBookingFromDateError] = useState<string | undefined>(); const onSubmit: SubmitHandler<FormFields> = async (data) => { await validating(data); await submitBooking(); }; const validating = async (data: FormFields) => { const timeNow = new Date(); const bookingFromDate = new Date(data.bookingfromdate); const bookingToDate = new Date(data.bookingtodate); if (dateIsBefore(bookingFromDate, timeNow) === true) { setBookingFromDateError("Date and time cannot before current datetime"); } else { setBookingFromDateError(undefined); } if (dateIsBefore(bookingToDate, timeNow) === true) { setBookingToDateError("Date and time cannot before current datetime"); } else { setBookingToDateError(undefined); } }; const submitBooking = async () => { if ( bookingFromDateError === undefined && bookingToDateError === undefined ) { console.log("Form is valid, can proceed"); // I can call api here } else { console.log("Form is not valid, cannot proceed"); // show error message cuz form is not valid } };
问题原因
React的useState状态更新是异步的,validating函数里调用setBookingFromDateError和setBookingToDateError后,状态并不会立刻更新。此时立刻调用submitBooking,读取到的还是更新前的旧状态,所以首次提交会判断错误。
解决方案
方案1:让验证函数直接返回验证结果
不依赖状态值判断表单有效性,在validating中直接返回验证状态,同时更新状态用于页面错误展示:
const [bookingToDateError, setBookingToDateError] = useState<string | undefined>(); const [bookingFromDateError, setBookingFromDateError] = useState<string | undefined>(); const onSubmit: SubmitHandler<FormFields> = async (data) => { const isValid = await validating(data); if (isValid) { await submitBooking(); } else { console.log("Form is not valid, cannot proceed"); } }; const validating = async (data: FormFields): Promise<boolean> => { const timeNow = new Date(); const bookingFromDate = new Date(data.bookingfromdate); const bookingToDate = new Date(data.bookingtodate); let fromError: string | undefined; let toError: string | undefined; if (dateIsBefore(bookingFromDate, timeNow) === true) { fromError = "日期和时间不能早于当前时间"; } if (dateIsBefore(bookingToDate, timeNow) === true) { toError = "日期和时间不能早于当前时间"; } setBookingFromDateError(fromError); setBookingToDateError(toError); return fromError === undefined && toError === undefined; }; const submitBooking = async () => { console.log("Form is valid, can proceed"); // 在这里调用API };
方案2:使用useEffect监听错误状态
如果必须依赖状态值判断,可通过useEffect监听错误状态变化,待状态更新后再执行提交逻辑:
const [bookingToDateError, setBookingToDateError] = useState<string | undefined>(); const [bookingFromDateError, setBookingFromDateError] = useState<string | undefined>(); const [isValidationDone, setIsValidationDone] = useState(false); const onSubmit: SubmitHandler<FormFields> = async (data) => { setIsValidationDone(false); await validating(data); setIsValidationDone(true); }; const validating = async (data: FormFields) => { const timeNow = new Date(); const bookingFromDate = new Date(data.bookingfromdate); const bookingToDate = new Date(data.bookingtodate); if (dateIsBefore(bookingFromDate, timeNow) === true) { setBookingFromDateError("日期和时间不能早于当前时间"); } else { setBookingFromDateError(undefined); } if (dateIsBefore(bookingToDate, timeNow) === true) { setBookingToDateError("日期和时间不能早于当前时间"); } else { setBookingToDateError(undefined); } }; useEffect(() => { if (isValidationDone) { if (bookingFromDateError === undefined && bookingToDateError === undefined) { submitBooking(); } else { console.log("Form is not valid, cannot proceed"); } setIsValidationDone(false); } }, [isValidationDone, bookingFromDateError, bookingToDateError]); const submitBooking = async () => { console.log("Form is valid, can proceed"); // 在这里调用API };
总结
优先推荐方案1,逻辑更直接,避免了异步状态更新带来的时序问题,同时能正常更新错误状态用于页面展示。
内容的提问来源于stack exchange,提问作者Marcus Neo
相关产品推荐
相关产品推荐

