useState 加载未在异步之前更新

时间:2021-06-16 06:38:40

标签: reactjs asynchronous use-state

我有一个函数 runValidation,它循环遍历一堆数字并进行一些计算。这可能需要一段时间,所以我想包含一个“isLoading”useState。

所以我做了一个简单的

const [isLoading, setIsLoading] = useState(false)

当我单击一个按钮时,我想将 isLoading 设置为 true,运行我的函数,然后执行 setIsLoading(false)。 所以我让我的验证函数异步,以及按钮处理程序

const handleClick = async () => {
    setIsLoading(true)
    const isValid = await runValidation()
    setIsLoading(false)
}

我的按钮组件很简单

<button onClick={() => handleClick()}>Run Validations</button>

然而,每次我尝试运行它时,在验证功能完成之前都没有设置加载。所以在“isLoading”变量上有一个 useEffect 。我得到 console.logs 显示

Running Validations
isLoading: true
isLoading: false

为什么我的“isLoading”直到异步之后才为真?有没有办法确定? 我尝试将 setIsLoading(true) 向下移动到 onClick 按钮 - 我尝试使用 .then() 而不是 await 等。 - 但是 isLoading 仅在异步函数之后设置为 true。

编辑:示例 - https://codesandbox.io/s/suspicious-cartwright-0y5jk

1 个答案:

答案 0 :(得分:1)

仅仅因为您声明了一个函数 async 并不意味着它实际上是一个异步函数,它只是允许您在内使用 await 关键字 em> 函数并隐式返回一个承诺。

在您的示例代码和框的情况下,runValidation 被声明为 async 但运行完全同步代码。

const runValidation = async () => {
  let result = 0;
  console.log("Running Validation");
  for (let i = 1; i <= 500000; i++) {
    if (i > 200000) {
      result = i;
      break;
    }
  }

  return result;
};

如果您实际上执行一些异步操作,那么您会注意到控制台日志是相同的。

示例:

const runValidation = async () => {
  console.log("Running Validation");

  const result = await new Promise((resolve) => {
    setTimeout(() => {
      let result = 0;
      for (let i = 1; i <= 500000; i++) {
        if (i > 200000) {
          result = i;
          break;
        }
      }
      resolve(result);
    }, 3000);
  });

  return result;
};
<块引用>
Running Validation // logged immediately in callback
isLoading true     // logged in useEffect next render cycle
isLoading false    // logged in useEffect ~3 seconds later

Edit usestate-loading-not-updating-before-async

为什么日志输出一样?

<块引用>
const handleClick = async () => {
  setIsLoading(true);
  await runValidation();
  setIsLoading(false);
};

在这里,您已将状态更新入队,然后立即调用 runValidation,这将控制台日志 "Running Validation" 然后 "isLoading true" 来自 useEffect,等待隐式 Promise (时间不长),然后另一个状态更新进入队列,"isLoading false"useEffect 控制台记录。

您的代码似乎完全符合我的预期。

如果您将验证日志移入异步逻辑,那么控制台日志输出可能更像您期望的,因为您现在给 React 状态一个机会在“异步”代码完成“触发”之前更新和重新渲染

const runValidation = async () => {
  const result = await new Promise((resolve) => {
    setTimeout(() => {
      console.log("Running Validation");
      let result = 0;
      for (let i = 1; i <= 500000; i++) {
        if (i > 200000) {
          result = i;
          break;
        }
      }
      resolve(result);
    }, 3000);
  });

  return result;
};

输出

<块引用>
isLoading true
Running Validation 
isLoading false