代码之家  ›  专栏  ›  技术社区  ›  dwjohnston

为什么我不能通过React中ref存储的resolve函数解析promise?

  •  -1
  • dwjohnston  · 技术社区  · 1 年前

    我试图创建一个promisify风格的钩子,这个想法是最终将其与Redux Toolkit一起使用,以允许使用新的React 19 use 钩子。

    以下是这件事的工作原理:

    function usePromise(input: {
        isLoading: true,
        data: null
    } | {
        isLoading: false,
        data: string;
    }) {
    
    
        // Store the resolve function in a ref 
        const resRef = useRef<(data: string) => void>(null);
    
        // Create a new promise 
        // Store the resolve function in the ref 
        const promiseRef = useRef(
            new Promise((res) => {
                resRef.current = res;
    
                //res("xxx") // 👈 this will resolve though
            })
        );
    
        // When input changes, if there is data, resolve the promise
        useEffect(() => {
            if (!input.isLoading) {
                resRef.current?.(input.data);
            }
    
        }, [input]);
    
        // Return the promise 
        return promiseRef.current;
    }
    

    用法如下:

    export function MyComponent() {
    
        const [value, setValue] = useState<null | string>(null);
    
        const prom = usePromise(value ? {
            isLoading: false,
            data: value
        } : {
            isLoading: true,
            data: null
        });
    
        prom.then((v) => alert(v))
    
        return <div >
    
            <button onClick={() => setValue("123")}>Click me</button>
        </div>
    }
    

    在这里,我希望当我们点击按钮时,promise会解决,我们会看到警报。然而,事实并非如此。

    这是怎么回事?

    我在这里复制了这个问题: https://github.com/dwjohnston/react-promise-issue

    1 回复  |  直到 1 年前
        1
  •  2
  •   Bergi    1 年前

    你的问题是,每次钩子运行时,你都在创建一个新的promise,设置 resRef.current 最后一个promise的解析器函数。然而,只有第一个承诺被传递给了 useRef 钩子存放在 promiseRef.current .

    为了解决这个问题, avoid recreating the promise :

    function usePromise(input) {
        const resRef = useRef();
        const promiseRef = useRef();
        if (!promiseRef.current) {
    //  ^^^^^^^^^^^^^^^^^^^^^^^^
            promiseRef.current = new Promise(resolve => {
                resRef.current = resolve;
            });
        }
        useEffect(() => {
            if (!input.isLoading) {
                resRef.current(input.data);
            }
        }, [input]);
        return promiseRef.current;
    }
    

    或者,使用 a state that is initialised with a callback :

    function usePromise(input) {
        const [{ promise, resolve }] = useState(() => Promise.withResolvers());
        useEffect(() => {
            if (!input.isLoading) {
                resolve(input.data);
            }
        }, [input]);
        return promise;
    }