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

React Hook返回Promise后resolve未触发问题排查

问题:React Hook返回的Promise无法触发.then回调

尝试实现一个返回Promise的React Hook,期望通过await直接获取异步值,不需要等待Hook解析完成后触发组件重新渲染,但实际遇到Promise的.then始终不执行的问题,推测resolve函数未正确触发。

问题代码

function App() {
  const { valPromise } = useSomeHook();
  const [state, setState] = React.useState();
  
  React.useEffect(() => {
    valPromise.then(r => {
        setState(r);
    });
  }, []);
  
  if (!state) return 'not resolved yet';
  return 'resolved: ' + state;
}

function useSomeHook() {
  const [state, setState] = React.useState();
  const resolve = React.useRef();
  const valPromise = React.useRef(new Promise((res) => {
    resolve.current = res;
  }));
  
  React.useEffect(() => {
    getValTimeout({ setState });
  }, []);
  
  React.useEffect(() => {
    if (!state) return;
    resolve.current(state);
  }, [state]);
  
  return { valPromise: valPromise.current, state };
}

function getValTimeout({ setState }) {
  setTimeout(() => {
    setState('the val');
  }, 1000);
}

可正常运行的对比测试代码

用普通函数实现的相同逻辑可以正常触发Promise的回调:

let resolve;

function initPromise() {
  return new Promise((res) => {
    resolve = res;
  });
}

function actionWithTimeout() {
  setTimeout(() => {
    resolve('the val');
  }, 2000);
}

const promise = initPromise();
actionWithTimeout();
promise.then(console.log);

问题根源

问题出在useSomeHook中resolve ref的初始化时机:
Promise的executor函数是同步执行的,当valPromise这个ref初始化时,会立刻执行Promise的回调,尝试把res赋值给resolve.current——但此时resolve这个useRef还没完成初始化(useRef的初始化发生在组件渲染阶段,和ref的赋值是同步顺序,但这里Promise的executor先于resolve ref的完成),导致resolve.current实际是undefined。后续调用resolve.current(state)时,相当于调用了一个不存在的函数,自然无法resolve Promise。

修复方案

调整Promise的创建时机,确保resolve ref初始化完成后再创建Promise,同时修正App组件的依赖:

修改后的useSomeHook

function useSomeHook() {
  const [state, setState] = React.useState();
  const resolve = React.useRef();
  const valPromise = React.useRef();

  React.useEffect(() => {
    // 确保resolve ref已初始化,再创建Promise并保存resolve函数
    valPromise.current = new Promise((res) => {
      resolve.current = res;
    });
    getValTimeout({ setState });
  }, []);

  React.useEffect(() => {
    if (!state) return;
    // 用可选链避免未初始化时调用报错
    resolve.current?.(state);
  }, [state]);

  return { valPromise: valPromise.current, state };
}

修改后的App组件useEffect

React.useEffect(() => {
  // 用可选链处理valPromise未初始化的情况
  valPromise?.then(r => {
      setState(r);
  });
}, [valPromise]);

修复逻辑说明

  1. 将Promise的创建移到useEffect中,此时resolve ref已经完成初始化,能正确捕获并保存resolve函数的引用。
  2. App组件的useEffect添加valPromise作为依赖,确保当Promise创建完成后才绑定.then回调,避免调用undefined的then方法。
  3. 使用可选链?.做安全调用,避免在ref未初始化时执行函数导致报错。

内容的提问来源于stack exchange,提问作者Abdul Ahmad

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 22:26:25