admin管理员组

文章数量:1429648

I'm trying to render a loading message after 1.5 seconds of waiting, if the thing is still loading:

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

The variable loading is a boolean in state returned by an Apollo hook, and setSleepMessage is hooked to state too:

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

For some reason my logic fails here; the sleep message is rendered after 1.5 seconds no matter whether loading has changed from true to false at that point. Why is this? Is its value in setTimeout evaluated right away even though the function is to be executed after the timer expires? Or am I missing something else here?

I'm trying to render a loading message after 1.5 seconds of waiting, if the thing is still loading:

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

The variable loading is a boolean in state returned by an Apollo hook, and setSleepMessage is hooked to state too:

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

For some reason my logic fails here; the sleep message is rendered after 1.5 seconds no matter whether loading has changed from true to false at that point. Why is this? Is its value in setTimeout evaluated right away even though the function is to be executed after the timer expires? Or am I missing something else here?

Share Improve this question asked Aug 13, 2020 at 8:04 jkajka 4671 gold badge6 silver badges17 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 2

You didn't clear the previous timeout.

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

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

You need to clean up your effects by returning a function to clear the timeout. I suggest also only initiating the timeout if loading is 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]);

Firstly, the query is loading so the value of loading is true, the setTimeout will be put into JS call stack and keep the value of loading is true, after 1,5s it is put into task queue. After some ticks, the call stack is empty, it will put the callback function inside setTimeout back to callstack and setSleepMessage is executed. So that why the sleep message is rendered after 1.5 seconds no matter whether loading is true or false. You should clearTimeout in useEffect to prevent this happens.

You can read more about event loop in JS to dive deeper.

本文标签: javascriptHow to conditionally change state in useEffect after a delay in ReactStack Overflow