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]);
修复逻辑说明
- 将Promise的创建移到useEffect中,此时
resolveref已经完成初始化,能正确捕获并保存resolve函数的引用。 - App组件的useEffect添加
valPromise作为依赖,确保当Promise创建完成后才绑定.then回调,避免调用undefined的then方法。 - 使用可选链
?.做安全调用,避免在ref未初始化时执行函数导致报错。
内容的提问来源于stack exchange,提问作者Abdul Ahmad
相关产品推荐
相关产品推荐

