简体   繁体   中英

How can I call useEffect to re-render upon a particular state change

I need the first useEffect below to re-render upon a change to the grid state. Specifically, I need the useEffect containing initializeGrid() to re-render once a change of state is registered for grid inside the changeWall function when I reassigned one of the grid object's values to true. I need this change to immediately re-render that initializeGrid() function

import React, { useState, useEffect } from "react";


const Pathfind = () => {

  const [grid, setGrid] = useState(false);


  useEffect(() => {
    initializeGrid();
  }, []);

  const initializeGrid = () => {

    grid = new Array(X);

    for (let i = 0; i < Y; i++) {
      grid[i] = new Array(Y);
    }

    setGrid(grid);

  };

  let isMouseDown = false;

  function changeWall(e) {
    let id = e.target.id;
    grid[id.y][id.x].isWall = true;
  }

  useEffect(() => {
    document.addEventListener("mousedown", function () {
      isMouseDown = true;

      document.onmouseup = () => {
        isMouseDown = false;
        document.removeEventListener("mouseover", changeWall);
      };

      if (mouseIsDown) {
        document.addEventListener("mouseover", changeWall);
      }

    });
  }, []);

 };

export default Pathfind;

Issue

The grid state is const, so attempting grid = new Array(X); will throw an error in initializeGrid . Perhaps you meant to declare a new variable to initialize/update state with.

const grid = new Array(X);

Answer

Use a second piece of state to indicate a "wall" was changed. Initial state of true will allow the initializeGrid function to run on the initial component mounting render.

const [wallChanged, setWallChanged] = useState(true);

useEffect(() => {
  if (wallChanged) {
    setWallChanged(false);
    initializeGrid();
  }
}, [wallChanged]);

Set wall changed state in the handler to trigger re-initialization.

function changeWall(e) {
  const { id } = e.target;
  grid[id.y][id.x].isWall = true; // *
  setWallChanged(true);
}

* Note: Be aware that grid[id.y][id.x].isWall = true; is a state mutation. Without much other context though I can't say for sure if this is an actual issue, but it is definitely a code smell.

Actually, since initializeGrid resets the grid state completely, this is probably not an issue, and really, the grid[id.y][id.x].isWall = true; line may not be completely unnecessary with the wallChanged state "flag".

A much simpler solution may just be to invoke initializeGrid directly in the handler. No extra state and the grid state is updated and component rerenders at least 1 render cycle earlier than my previous solution.

function changeWall(e) {
  initializeGrid();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM