React中如何等待onBlur回调(多请求)完成后再触发onClick事件
解决思路
以下是几种React生态下的可行方案,针对你的异步时序问题逐一解决:
方法1:用Ref跟踪失焦异步任务的Promise
通过useRef存储当前正在执行的失焦异步任务Promise,点击提交时先等待该Promise完成,确保所有异步操作和状态更新结束后再执行提交逻辑。
const [input, setInput] = useState({ id: '0', value: undefined }); // 存储当前失焦的异步任务Promise const blurTaskRef = useRef(null); const handleOnBlur = useCallback(async (event) => { const newValue = event.target.value; // 将当前异步任务存入ref blurTaskRef.current = (async () => { try { await validateInput(input.id, newValue); await doAnotherAsynchronousTask(); setInput({ id: input.id, value: newValue }); } finally { // 任务完成后清空ref blurTaskRef.current = null; } })(); await blurTaskRef.current; }, [validateInput, doAnotherAsynchronousTask, input.id]); const handleOnClick = useCallback(async () => { // 如果有正在执行的失焦任务,先等待它完成 if (blurTaskRef.current) { await blurTaskRef.current; } // 此时状态已更新,提交最新值 await submitNewInput(input); }, [submitNewInput, input]); return ( <div> <input value={input.value} onBlur={handleOnBlur} /> <button onClick={handleOnClick}>Submit</button> </div> );
方法2:手动触发失焦并等待异步完成
在提交按钮点击时,先手动让输入框失焦(确保handleOnBlur触发),然后等待失焦异步任务完成后再提交,结合状态跟踪处理进度。
const [input, setInput] = useState({ id: '0', value: undefined }); const [isBlurProcessing, setIsBlurProcessing] = useState(false); const inputRef = useRef(null); const handleOnBlur = useCallback(async (event) => { const newValue = event.target.value; setIsBlurProcessing(true); try { await validateInput(input.id, newValue); await doAnotherAsynchronousTask(); setInput({ id: input.id, value: newValue }); } finally { setIsBlurProcessing(false); } }, [validateInput, doAnotherAsynchronousTask, input.id]); const handleOnClick = useCallback(async () => { // 手动让输入框失焦,触发handleOnBlur inputRef.current?.blur(); // 等待失焦处理完成 while (isBlurProcessing) { // 用微任务等待状态更新 await new Promise(resolve => setTimeout(resolve, 0)); } await submitNewInput(input); }, [submitNewInput, input, isBlurProcessing]); return ( <div> <input ref={inputRef} value={input.value} onBlur={handleOnBlur} /> <button onClick={handleOnClick}>Submit</button> </div> );
方法3:将失焦逻辑整合到提交流程中
提取失焦时的异步逻辑为独立函数,提交时主动执行该逻辑,避免依赖事件触发的时序问题,同时保证提交前完成所有必要的异步操作。
const [input, setInput] = useState({ id: '0', value: undefined }); // 提取失焦时的异步逻辑为独立函数 const processInput = useCallback(async (newValue) => { await validateInput(input.id, newValue); await doAnotherAsynchronousTask(); setInput({ id: input.id, value: newValue }); }, [validateInput, doAnotherAsynchronousTask, input.id]); const handleOnBlur = useCallback(async (event) => { await processInput(event.target.value); }, [processInput]); const handleOnClick = useCallback(async () => { // 先处理当前输入值(和失焦逻辑一致) await processInput(input.value); // 再提交最新状态 await submitNewInput(input); }, [submitNewInput, input, processInput]); return ( <div> <input value={input.value} onBlur={handleOnBlur} /> <button onClick={handleOnClick}>Submit</button> </div> );
方法4:使用useReducer管理异步流程
对于复杂表单场景,用useReducer统一管理状态和异步操作的流转,更清晰地控制时序逻辑。
const inputReducer = (state, action) => { switch (action.type) { case 'START_PROCESSING': return { ...state, isProcessing: true }; case 'UPDATE_INPUT': return { ...state, value: action.payload, isProcessing: false }; default: return state; } }; const initialState = { id: '0', value: undefined, isProcessing: false }; const MyForm = () => { const [state, dispatch] = useReducer(inputReducer, initialState); const processInput = useCallback(async (newValue) => { dispatch({ type: 'START_PROCESSING' }); await validateInput(state.id, newValue); await doAnotherAsynchronousTask(); dispatch({ type: 'UPDATE_INPUT', payload: newValue }); }, [validateInput, doAnotherAsynchronousTask, state.id]); const handleOnBlur = useCallback(async (event) => { await processInput(event.target.value); }, [processInput]); const handleOnClick = useCallback(async () => { // 等待处理完成 if (state.isProcessing) { await new Promise(resolve => { const unsubscribe = useEffect(() => { if (!state.isProcessing) { resolve(); unsubscribe(); } }, [state.isProcessing]); }); } // 如果输入框值和状态不一致,先同步处理 if (inputRef.current?.value !== state.value) { await processInput(inputRef.current.value); } await submitNewInput(state); }, [submitNewInput, state, processInput]); const inputRef = useRef(null); return ( <div> <input ref={inputRef} value={state.value} onBlur={handleOnBlur} /> <button onClick={handleOnClick}>Submit</button> </div> ); };
内容的提问来源于stack exchange,提问作者kitimenpolku
相关产品推荐
相关产品推荐

