0

Why is it necesary to wrap the settimeout in a useEffect hook for this custom hook?

function useWait (delay) {
  const [show, setShow] = React.useState(false)

  React.useEffect(() => {
    const id = window.setTimeout(() => {
      setShow(true)
    }, delay)

    return () => window.clearTimeout(id)
  }, [delay])

  return show
}
Ertan Hasani
  • 1,675
  • 12
  • 27
  • You need to show two examples, and then ask "Why its necessary to wrap..", in other words, what is the other option you think is right? – Dennis Vash Dec 04 '19 at 13:59
  • So that when you change the delay, your setTimeout is updated -> this way it's dynamic. – Konstantin Dec 04 '19 at 14:00
  • Does this answer your question? [React hooks - right way to clear timeouts and intervals](https://stackoverflow.com/questions/53090432/react-hooks-right-way-to-clear-timeouts-and-intervals) – norbitrial Dec 04 '19 at 14:00
  • Short answer is that if it is not in a useEffect, a new setTimeout will be created every time the component rerenders. See https://reactjs.org/docs/hooks-effect.html#effects-with-cleanup for an explanation. – Cal Irvine Dec 04 '19 at 16:25

1 Answers1

0

If you don't put a setTimeout or setInterval inside useEffect, it will be reinitialized and not cleaned up every time your component rerenders.

Look at the below example. Every time you increment the counter by pressing the button, the parent component rerenders, which trickles rerenders to all children (including my setTimeout component). Press the button a few times, you will see the component rerenders several times, and then you will see the 'timeout finished' several times.

Then below I have added a snippet with useEffect, you will see the timeout only runs the one time.

Does this answer your question?

edit: Here is a codesandbox to try them out https://codesandbox.io/s/boring-liskov-4kjgm?fontsize=14&hidenavigation=1&theme=dark

without useEffect

import React, {useState} from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  const [counter, increment] = useState(0);
  const doIncrement = () => increment(counter + 1);
  return (
    <div className="App">
      <Counter counter={counter} increment={doIncrement} />
      <OtherComponent />
    </div>
  );
}

const Counter = ({counter, increment}) => {
  return <button onClick={()=>increment()}>{counter}</button>
}

const OtherComponent = () => {
  console.log('I rerendered');
  const timeout = setTimeout(() => console.log('timeout finished'), 1000)
  return <h1> Hello world </h1>
}



const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

With useEffect

import React, {useState, useEffect} from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  const [counter, increment] = useState(0);
  const doIncrement = () => increment(counter + 1);
  return (
    <div className="App">
      <Counter counter={counter} increment={doIncrement} />
      <OtherComponent />
    </div>
  );
}

const Counter = ({counter, increment}) => {
  return <button onClick={()=>increment()}>{counter}</button>
}

const OtherComponent = () => {
  console.log('I rerendered');
  useEffect(() => {
     const timeout = setTimeout(() => console.log('timeout finished'), 1000)
     return () => clearTimeout(timeout)
  }, [])

  return <h1> Hello world </h1>
}



const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Cal Irvine
  • 1,104
  • 8
  • 17