Juan Diego
Juan Diego

Reputation: 1466

useEffect creates an infinite loop but I dont understand it completely

I have had in the past similar problems of infinite recursion, but I am not understanding what I am doing wrong. Here you can have the important parts of my function I have these states on top

  const PREV_ICON = <span aria-hidden="true" className="carousel-control-prev-icon" />;
  const NEXT_ICON = <span aria-hidden="true" className="carousel-control-next-icon" />;
  const [control, setControl] = useState({ prev: null, next: null });

So basically the NEXT_ICON is a constant declared on top. If I have more than 4 items on my carousel I want to show the next icon

useEffect(() => {
    const fetchSkills = async () => {
      try {
        const result = await skillsInformationService.getSkills(sessionUserInfo.jigsawId);
        setSkillGroups(result);
        const slices = _(result).keys().chunk(4).value();
        setGroupSlices(slices);
        setLoading(false);
        setControl({
          prev: null,
          next: (_(result).size() < 4) ? null : NEXT_ICON
        });
      } catch (error) {
        setSkillGroups([]);
        setGroupSlices([]);
      }
    };
    fetchSkills();
  }, [sessionUserInfo.jigsawId, NEXT_ICON]);

VSCode added by default the NEXT_ICON to the array of params of useEffect, I didn't realize until I saw that useEffect kept on being called. So then I delete NEXT_ICON and it was only being called once, but if I do that the setControl({}) seems to not be called. But I might have misunderstood the use of useEffect, why is useEffect being called if NEXT_ICON is a constant that doesn't change. And I think that if I want to use a variable inside useEffect I have to call it the params right. So I do need to put NEXT_ICON next to sessionUserInfo.jigsawId, otherwise it is null? Right?

Upvotes: 0

Views: 90

Answers (2)

falinsky
falinsky

Reputation: 7428

You're having an infinite loop because the identities of objects stored in your constants PREV_ICON and NEXT_ICON change for every single render (as soon as you're using functional component) and while having NEXT_ICON as a dependency for useEffect hook - you're just basically having a new object every time (which changes for every render).

Also storing the whole components (like PREV_ICON and NEXT_ICON) to the internal state of the object might not be the best decision.

Possible options to fix this situation:

  1. You can consider changing the kind of data to store in the component state. For example instead of components you can use booleans like PREV_ICON_VISIBLE, NEXT_ICON_VISIBLE and based on their values later render whatever content you want.
  2. You can extract PREV_ICON and NEXT_ICON out of your functional component.
  3. You can use useRef hooks to preserve identities of PREV_ICON and NEXT_ICON (which might not be the most elegant way).

Upvotes: 1

Gabriele Petrioli
Gabriele Petrioli

Reputation: 195992

It is a constant in the block it is defined. Since the block in your case is the functional component, each time you re-render the component the function is re-run, and thus you create a new PREV_ICON,NEXT_ICON, and the useEffect will be executed.

The solution is to move the two constants outside of the function.

const PREV_ICON = <span aria-hidden="true" className="carousel-control-prev-icon" />;
const NEXT_ICON = <span aria-hidden="true" className="carousel-control-next-icon" />;
  
const YourComponent = () => {
  const [control, setControl] = useState({ prev: null, next: null });
  
  ....
  
  
  useEffect( ... ,[sessionUserInfo.jigsawId, NEXT_ICON]); // whether you have NEXT_ICON here, or not, now makes no difference.

Upvotes: 2

Related Questions