Home > Mobile >  Why change State inside setTimeout make an infinity loop
Why change State inside setTimeout make an infinity loop

Time:03-29

I have a functional component and I want to change State inside a setTimeout, but I don't know why it causes the component to be in an infinite loop!

Here is my Functional Component:

import { useState } from "react";

function Card() {
  const [booksState, setBooks] = useState({
    books: [
      { name: "Harry", id: 1 },
      { name: "Potter", id: 2 },
      { name: "John", id: 3 },
    ],
  });

  console.log("test");

  setTimeout(() => {
    let newBooks = [
      { name: "test1", id: 4 },
      { name: "test2", id: 5 },
      { name: "test3", id: 6 },
    ];

    setBooks({
      books: [...booksState.books, ...newBooks],
    });
  }, 2000);

  return <div>TEST</div>;
}

export default Card;

The console log:

enter image description here

CodePudding user response:

Set Timeout runs every time the component renders. This means every time the state changes, it starts a new timer. Assuming you only want to run the timer when the component first renders, you should use the useEffect hook as follows:

import { useState } from "react";

function Card() {
  const [booksState, setBooks] = useState({
    books: [
      { name: "Harry", id: 1 },
      { name: "Potter", id: 2 },
      { name: "John", id: 3 },
    ],
  });

  console.log("test");

  useEffect(() => {
    setTimeout(() => {
      let newBooks = [
        { name: "test1", id: 4 },
        { name: "test2", id: 5 },
        { name: "test3", id: 6 },
      ];

      setBooks({
        books: [...booksState.books, ...newBooks],
      });
    }, 2000)
  }, []);

  return <div>TEST</div>;
}

export default Card;

CodePudding user response:

You need to call setTimeout inside a useEffect to avoid infinite calling of setTimeout.

In the current implementation, setTimeout gets called again after the first timeout completes (due to rerendering) which causes it to exponentially accumulate the timeout calls.

  useEffect(() => {
    setTimeout(() => {
      let newBooks = [
        { name: "test1", id: 4 },
        { name: "test2", id: 5 },
        { name: "test3", id: 6 }
      ];

      setBooks({
        books: [...booksState.books, ...newBooks]
      });
    }, 2000);
  }, []);

Edit serene-mclean-pirz2i

CodePudding user response:

In your current implementation setTimeout runs every time the component renders means on every state update so because of that it's getting called again and again. So, you need to call setTimeout inside of useEffect. And don't forget to clearTimeout in the useEffect's cleanup function.

useEffect(() => {
   const myTimeout = setTimeout(() => {
      let newBooks = [
        { name: "test1", id: 4 },
        { name: "test2", id: 5 },
        { name: "test3", id: 6 },
      ];

      setBooks({
        books: [...booksState.books, ...newBooks],
      });
    }, 2000)
  
   return () => {
     clearTimeout(myTimeout)
   }

  }, []);

  • Related