You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 16:01:40