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

时间:2020-08-13 08:04:24

标签: javascript reactjs

等待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);
  // ...

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

3 个答案:

答案 0 :(得分:1)

您没有清除上一个超时。

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

  return () => clearTimeout(timeoutId);
}, [loading]);

答案 1 :(得分:1)

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

您可以在JS中阅读有关event loop的更多信息,以进行更深入的研究。

答案 2 :(得分:1)

您需要通过返回清除超时的函数来清理效果。我建议也仅在loading为true时才启动超时。

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]);