skip to Main Content

I am calling setState within useEffect and it’s triggering infinitely. I’m having a hard time understanding why. The problem is with the third useEffect that is dependent on picked.

import GameCSS from './game.module.css'
import Dog from "./dog/dog"
import { useState, useEffect, useRef } from 'react'

function Game () {
  const [dogs, setDogs] = useState<string[]>([]);
  const [picked, setPicked] = useState<boolean>(false);
  const [fave, setFave] = useState<string>('');
  const [images, setImages] = useState<string[]>();

  useEffect(() => {
    fetch('https://dog.ceo/api/breeds/image/random/10')
      .then(response => response.json())
      .then(data => setDogs(data.message));
  }, []);

  useEffect(() => {
    setImages([dogs[0], dogs[1]]);
  }, [dogs]);

  useEffect(() => {
    setImages([dogs[1], dogs[2]]);
  }), [picked];

  if (images) {
    return (
      <>
        <div className={GameCSS.dogs}>
          <Dog
            image={images[0]}
            setFave={setFave}
            picked={picked}
            setPicked={setPicked}
          />
          <Dog
            image={images[1]}
            setFave={setFave}
            picked={picked}
            setPicked={setPicked}
          />
        </div>  
      </>
    )
  }
}

export default Game
import DogCSS from './dog.module.css'

interface info {
  image: string;
  setFave: (image: string) => void;
  picked: boolean;
  setPicked: (toggle: boolean) => void
}

function Dog (props:info) {
  function setFave() {
    props.setFave(props.image);
    props.setPicked(!props.picked);
  }
    
  return (
    <img
      className={DogCSS.image}
      src={props.image}
      onClick={setFave}
    >
    </img>   
  )
}

export default Dog

Warning Message: game.tsx:23 Warning: Maximum update depth exceeded. This can happen when a component calls setState inside
useEffect, but useEffect either doesn’t have a dependency array, or
one of the dependencies changes on every render.
at Game (http://127.0.0.1:5173/src/components/game/game.tsx?t=1689969940032:23:27)
at div
at App

What is supposed to happen is that when the user clicks on an image it toggles the picked state and triggers the images to update.

I tried removing picked dependency from the offending useEffect and that did not solve it. I tried doing something other than setImages and that fixes it, but what I need to do is update the images array when the picked flag is toggled so I need to find a way or redesign my solution. I tried to figure out how to set the offending useEffect to not run on the first render, but I became a bit overwhelmed and didn’t understand.

2

Answers


  1. Chosen as BEST ANSWER

    Besides the suboptimal design, the infinite useEffect behavior is caused by a misplaced bracket as pointed out in the comments. First snippet is incorrect, second is correct. My friend pointed out that chatGPT can be good at finding these bugs and it did find it when I tried it there.

    useEffect(() => {
        setImages([dogs[1], dogs[2]]);
    }),[picked];
    
    useEffect(() => { 
        setImages([dogs[1], dogs[2]]); 
    }, [picked]);
    

  2. There is no need of second and third useEffect in your code you can call setImages in first useEffect where you call setDogs.
    and also no need to pass all the props to component you can just pass the image and on an onClick method to get the event.

    I optimize your code below

    Game

        import GameCSS from './game.module.css'
    import Dog from "./dog/dog"
    import { useState, useEffect, useRef } from 'react'
    
    function Game() {
      const [dogs, setDogs] = useState<string[]>([]);
      const [fave, setFave] = useState<string>('');
      const [count, setCount] = useState<number>(2);
      const [images, setImages] = useState<string[]>();
    
      useEffect(() => {
        fetch('https://dog.ceo/api/breeds/image/random/10')
          .then(response => response.json())
          .then(data => {
            setDogs(data.message)
            setImages(data.message.slice(0,2));
          });
      }, []);
    
      const onPressDog = (image: any) => {
        setFave(image);
        setImages(dogs.slice(count, count + 2));
        setCount(count+2)
      }
      if (images) {
        return (
          <div className={GameCSS.dogs}>
            {
              images.map(image => <Dog
                image={image}
                onPress={() => onPressDog(image)}
              />
              )
            }
          </div>
        )
      }
      return null;
    }
    
    export default Game
    

    Dog

    import DogCSS from './dog.module.css'
    
    interface info {
      image: string;
      onPress: () => void;
    }
    
    function Dog(props: info) {
      return (
        <img
          className={DogCSS.image}
          src={props.image}
          onClick={props.onPress}
        >
        </img>
      )
    }
    
    export default Dog
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search