javascript 无法检索React.js中异步函数内的当前状态

pw136qt2  于 2023-01-29  发布在  Java
关注(0)|答案(3)|浏览(133)

我已经在顶层组件(App)中创建了一些状态,但是当更新此状态时,useEffect()中定义的异步函数似乎没有读取更新的状态(它仍然使用以前的值),详细信息如下:
我试图检索在useEffect()中定义的异步函数toggleProcessing中的const processing的状态,以便当processing变为false时,异步函数退出while循环,但是,似乎当processing更新为false时,while循环仍然继续执行。
行为应如下所示:按下"开始处理"按钮应每两秒控制台记录一次"处理...",当再次按下同一按钮(现在标记为"停止处理")时,控制台应记录"停止处理"。但是,实际上,控制台从不记录"停止处理",而是永远连续记录"处理"。
下面是代码:

import React, { useState, useEffect} from 'react'

const App = () => {

    const [processing, setProcessing] = useState(false)

    const sleep = (ms) => {
        return new Promise(resolve => setTimeout(resolve, ms))
    }

    useEffect(() => {
        const toggleProcessing = async () => {
            while (processing) {
                console.log('Processing...')
                await sleep(2000);
            }
            console.log('Stopping Processing')
        }
        if (processing) {
            toggleProcessing()      // async function
        }
    }, [processing])

    return (
        <>
            <button onClick={() => setProcessing(current => !current)}>{processing ? 'Stop Processing' : 'Begin Processing'}</button>
        </>
    )
}

export default App;

它实际上只是能够在异步函数中读取processing的更新状态,但我还没有找到这样做的方法,尽管阅读了类似的帖子。
先谢谢你!

pqwbnv8z

pqwbnv8z1#

如果你想在使用超时时访问一个状态,最好保留一个对该变量的引用,你可以使用useRef钩子来实现这一点,只需添加一个带有处理值的ref,并记得更新它。

const [processing, setProcessing] = useState<boolean>(false);
const processingRef = useRef(null);

useEffect(() => {
    processingRef.current = processing;
}, [processing]);
icomxhvb

icomxhvb2#

我很感兴趣这个是如何工作的,以及基于公认答案的最终解决方案是什么。我根据Dan Abramov的useInterval钩子提出了一个解决方案,并认为这个解决方案以及一些相关资源的链接可能对其他人有用。
我很好奇,你决定使用setTimeout并引入async/awaitwhile循环,而不是使用setInterval,有什么特别的原因吗?我想知道其中的含义。你会在卸载所有正在运行的计时器时处理clearTimeout吗?你的最终解决方案是什么样子的?

带useInterval的演示/解决方案

https://codesandbox.io/s/useinterval-example-processing-ik61ho

import React, { useState, useEffect, useRef } from "react";

const App = () => {
  const [processing, setProcessing] = useState(false);

  useInterval(() => console.log("processing"), 2000, processing);

  return (
    <div>
      <button onClick={() => setProcessing((prev) => !prev)}>
        {processing ? "Stop Processing" : "Begin Processing"}
      </button>
    </div>
  );
};

function useInterval(callback, delay, processing) {
  const callbackRef = useRef();

  // Remember the latest callback.
  useEffect(() => {
    callbackRef.current = callback;
  }, [callback]);

  // Set up the interval.
  useEffect(() => {
    function tick() {
      callbackRef.current();
    }
    if (delay !== null && processing) {
      let id = setInterval(tick, delay);

      console.log(`begin processing and timer with ID ${id} running...`);

      // Clear timer on clean up.
      return () => {
        console.log(`clearing timer with ID ${id}`);
        console.log("stopped");
        clearInterval(id);
      };
    }
  }, [delay, processing]);
}

export default App;

相关链接

Dan Abramov - Making setInterval Declarative with React Hooks
SO Question: React hooks - right way to clear timeouts and intervals

q3qa4bjr

q3qa4bjr3#

下面是工作代码:

import React, { useState, useEffect, useRef} from 'react'

const App = () => {

    const [processing, setProcessing] = useState(false)
    const processingRef = useRef(null);

    const sleep = (ms) => {
        return new Promise(resolve => setTimeout(resolve, ms))
    }

    useEffect(() => {
        const toggleProcessing = async () => {
            while (processingRef.current) {
                console.log('Processing')
                await sleep(2000);
            }
            console.log('Stopping Processing')
        }
        processingRef.current = processing;
        if (processing) {
            toggleProcessing()      // async function
        }
    }, [processing])

    return (
        <>
            <button onClick={() => setProcessing(current => !current)}>{processing ? 'Stop Processing' : 'Begin Processing'}</button>
        </>
    )
}

export default App;

相关问题