在 React useEffect 中,你如何在长 运行 代码之间偷偷更新状态?

In React useEffect, how do you sneak state updates in between long running code?

我无法在 longRunningCode 执行之前显示我的加载状态。我试着让它异步无济于事。

const [loadingStatus, setLoadingStatus] = useState(undefined);
const [myAction, setMyAction] = useState(undefined);

const longRunningCode = () => {
  const file = // synchronous code to generate a gzipped File object
  return file;
}

// also tried
// const longRunningCode = async () => {
// ...

useEffect(() => {
  if (myAction) {
    setLoadingStatus('Do the thing...')
    const result = longRunningCode()
    // also tried `await` with async version
    // ...
    setLoadingStatus(undefined)
    setMyAction(undefined)
  }
}, [myAction])

//...

return (
  <div>
    <p>{loadingStatus}</p>
    <button onClick={() => setMyAction({})}>Generate file</button>
  </div>
)

useEffect 回调只允许 return undefined 或析构函数。在您的示例中,您传递了一个 return 是 Promise 的异步函数。它可能会或可能不会 运行,但您会看到 React 警告说 useEffect 回调是同步 运行。

相反,定义一个异步函数,然后调用它。

const [loadingStatus, setLoadingStatus] = useState(undefined);
const [myAction, setMyAction] = useState(undefined);

useEffect(() => {
  const doAction = async () => {
    if (myAction) {
      setLoadingStatus('Do the thing...');
      const result = await longRunningCode();
      // ...
      setLoadingStatus(undefined);
      setMyAction(undefined);
    }
  };

  doAction();
}, [myAction]);

//...

return <p>{loadingStatus}</p><button onClick={() => setMyAction({})} />

否则,你写的就可以了。

我在想一些事情:

const [loadingStatus, setLoadingStatus] = useState(undefined);
const [myAction, setMyAction] = useState(undefined);
const longRunningCode = () => {
 return new Promise(resolve, reject)=>{
  const file = // synchronous code to generate a File object
  resolve(file);
 }
}

useEffect(() => {
  if (myAction) {
    setLoadingStatus('Do the thing...')
    longRunningCode().then(file=>{
      // ...
      setLoadingStatus(undefined)
      setMyAction(undefined)
    })
  }
}, [myAction])

//...

return <p>{loadingStatus}</p><button onClick={() => setMyAction({})} />

**编辑:** 使用 setTimeout

const [loadingStatus, setLoadingStatus] = useState(undefined);
const [myAction, setMyAction] = useState(undefined);
const longRunningCode = () => {
  const file = // synchronous code to generate a File object
  return file
 }
}

useEffect(() => {
  if (myAction) {
    setLoadingStatus('Do the thing...')
     
     //a 0 second delay timer waiting for longrunningcode to finish
     let timer = setTimeout(() =>{
      longRunningCode()
      setLoadingStatus(undefined)
      setMyAction(undefined)
     }, 0);
      // clear Timmer on unmount
      return () => {
        clearTimeout(timer);
      };
    
  }
}, [myAction])

//...

return <p>{loadingStatus}</p><button onClick={() => setMyAction({})} />