开发者问题收集

如何在 React 中延迟后有条件地改变 useEffect 中的状态?

2020-08-13
4816

如果内容仍在加载,我尝试在等待 1.5 秒后呈现加载消息:

useEffect(() => {
  setTimeout(() => {
    if (loading) setSleepMessage("The server seems to be sleeping. Waking it up, wait a second...");
  }, 1500);
}, [loading]);

变量 loading 是 Apollo 钩子返回的状态中的布尔值,并且 setSleepMessage 也挂钩到状态:

import { useQuery } from "@apollo/client";
import React, { useState, useEffect } from "react";
// ...
  const [ sleepMessage, setSleepMessage ] = useState<string>();
  const { data , loading } = useQuery(SOME_QUERY);
  // ...

由于某种原因,我的逻辑在这里失败了;睡眠消息会在 1.5 秒后呈现,无论此时 loading 是否已从 true 更改为 false 。这是为什么?即使该函数要在计时器到期后执行,也会立即评估其在 setTimeout 中的值吗?还是我在这里遗漏了其他东西?

3个回答

您需要通过返回一个函数来清除超时,从而清理效果。我建议仅在 loading 为真时才启动超时。

useEffect(() => {
  let timerId;

  if (loading) {
    timerId = setTimeout(() => {
      setSleepMessage(
        "The server seems to be sleeping. Waking it up, wait a second..."
      );
    }, 1500);
  }
  return () => clearTimeout(timerId);
}, [loading]);
Drew Reese
2020-08-13

您没有清除之前的超时。

useEffect(() => {
  const timeoutId = setTimeout(() => {
    if (loading) setSleepMessage("The server seems to be sleeping. Waking it up, wait a second...");
  }, 1500);

  return () => clearTimeout(timeoutId);
}, [loading]);
Danilo Gacevic
2020-08-13

首先,查询正在加载,因此 loading 的值为 true,setTimeout 将被放入 JS 调用堆栈并保持 loading 的值为 true,1.5 秒后将其放入任务队列。经过几个 ticks 后,调用堆栈为空,它会将 setTimeout 中的回调函数放回调用堆栈并执行 setSleepMessage 。因此,无论加载是 true 还是 false,睡眠消息都会在 1.5 秒后呈现。您应该在 useEffect 中使用 clearTimeout 来防止这种情况发生。

您可以阅读有关 JS 中的 事件循环 的更多信息以深入了解。

Michael
2020-08-13