Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to use useEffect/state/variables properly without user interaction?

My goal is to set up a game loop but a simple test isn’t working as expected. In the following component, I am trying the useEffect hook to increment food. I expect to see "Food: 1". Instead I see "Food: 0". When I inspect the component with the dev tools, I can see that food is 2. I’ve discovered that the component mounts, increments food, unmounts, mounts again and increments food once more.

I have two questions:

  1. Can I do something about the double mount? (like prevent it or wait until the final mount with a nested component perhaps?)
  2. Why does the displayed food count still equal zero? Is it because game inside <span>Food: {game.food}</span> still refers to the initial instance? If so, how do I get the latest instance?

Component:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

import React from "react";

class Game {
  food = 0;
}

export default function App() {
  const [game, setGame] = React.useState(new Game());

  React.useEffect(() => {
    setGame((game) => {
      game.food += 1;
      return game;
    });
  });

  return <span>Food: {game.food}</span>;
}

>Solution :

Don’t Mutate State Objects

React uses reference comparisons and expects the reference of the root state object to change if any data within it has changed.

For Example:

// DON'T
setGame((game) => {
    // mutate and return same object
    game.food += 1;
    return game;
});

// DO
setGame((current) => {
    // create new object with updated food value
    return {
        ...current,
        food: current.food + 1
    };
});

Using the same reference will cause components to not update as expected.

useEffect Dependency Array

A useEffect without a dependency array will trigger every time the component renders.

If you wish for the useEffect to only trigger on mount provide an empty dependency array.

For Example:

// Every Render
useEffect(() => {
    alert('I trigger every render');
});

// On Mount
useEffect(() => {
    alert('I trigger on mount');
}, []);

// Everytime the reference for game changes
useEffect(() => {
    alert('I trigger everytime the game state is update');
}, [game]);
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading