Home > Back-end >  React setState not updating the UI
React setState not updating the UI

Time:01-11

This seems too weird for me being the only one with this problem... But let me explain:

I'm building a react app that is the Game of Life (you might have heard of it). I'm using a simple Form component that iterates over an Array of Arrays basically creating a 20 by 20 field. There's a button as well, which starts the next iteration.

Loading the data & getting the data to my LandParcel.js where I start calculating the next step works perfectly fine. The data is even loaded to the state, but it won't update the UI. Why is that?

App.js (the starting point for the app)

import { useState } from 'react';
import './App.css';
import { LandParcel } from './components/core/LandParcel';
import { Form } from './components/ui/Form';


function App() {

  // set the grid with random variables
  const [ grid, setGrid ] = useState(LandParcel.createForm)

  console.log('rerender because grid changed', grid)

  function showGrid(grid) {
    console.log('grid to be handed over to Form = ', grid)
  }

  return (
    <div className="container mt-4">
      <h1 className="text-center" onClick={() => showGrid(grid)}>Game of Life</h1>

      {/* load the grip, pass the states forward */}
      <Form
        grid={ grid }
        setGrid={ setGrid }
      />

    </div>
  );
}

export default App;

This then forwards the data & the setGrid this Form.jsx:


import { LandParcel } from "../core/LandParcel"

export const Form = ({ grid, setGrid }) => {
    
    // handles the click on the input button
    function handleInput(e) {

        e.preventDefault();
        LandParcel.renderNextStep(grid, setGrid)
    }

    // debugging only
    function giveInfo(status, x, y) {
        console.log(status, x, y);
    }

    return(
        <form className="form">

            <div className="parcelForm">
                {grid.map((row, x) => row.map((col, y) => (
                    <div className={grid[x][y].status ? "alive" : "dead"} onClick={() => giveInfo(grid[x][y], x, y)}/>
                    ))
                )}
            </div>

            <input type="submit" value="Start" className="btn btn-danger btn-block" onClick={handleInput}></input>
          </form>
    )
}

The handleInput function then calls a method in LandParcel.js, which after calculating tries to set the state like this:


export class LandParcel {

    constructor(status) {
        this.status = status;
    }


    static createForm() {
        const rows = [];
        for (let i = 0; i < 20; i  ) {
            rows.push(Array.from(Array(20), () => new LandParcel((Math.random() > 0.9 ? 1 : 0)))); 
        }
        
        return rows;
    }


    static renderNextStep(grid, setGrid) {

        let gridCopy = grid
        
        // Calculating the next step in here

        console.log('updated grid', gridCopy)
        setGrid(gridCopy)
        
    }

    // for debugging only, creates a new grid with all values set to 0
    static setCustomStep() {
        const rows = [];
        for (let i = 0; i < 20; i  ) {
            rows.push(Array.from(Array(20), () => new LandParcel(0))); 
        }

        return rows;
    }

}

The weird thing to me is, that when I call setGrid(setCustomStep) using the helper funciton that creates a new grid with all values set to 0, it updates the UI - but not if I call setGrid(gridCopy).

I'm sure I'm missing something, but I can't figure out what. Any help/hint is highly appreciated! :)

CodePudding user response:

I think that's happening because you are trying to update the state directly via the gridCopy variable.

When you equal grid to gridCopy, it only passes a reference to the grid (state) rather than storing a new variable in the memory Check out this article for more information

A solution is to deeply copy the grid into gridCopy which will create a new variable in the memory rather than passing a reference to the grid

let gridCopy = [...grid]
  • Related