2

I can't seem to to get reed of this error for some reason; I've looked into it, callbacks, including the code in the useEffect, but it isn't working for me (or I'm doing it wrong).

Here is the code:

import React, { useEffect, useState } from "react";
import "../css/main.css";
import Node from "./node";

const Pathfinder = () => {
  const START_NODE_ROW = 10;
  const START_NODE_COL = 15;
  const FINISH_NODE_ROW = 10;
  const FINISH_NODE_COL = 35;

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

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

  const getGrid = () => {
    setGrid(getInitGrid());
  };

  const getInitGrid = () => {
    const grid = [];
    for (let row = 0; row < 20; row++) {
      const currentRow = [];
      for (let col = 0; col < 50; col++) {
        currentRow.push(createNode(col, row));
      }
      grid.push(currentRow);
    }
    return grid;
  };

  const createNode = (col, row) => {
    return {
      col,
      row,
      isStart: row === START_NODE_ROW && col === START_NODE_COL,
      isFinish: row === FINISH_NODE_ROW && col === FINISH_NODE_COL,
      distance: Infinity,
      isVisited: false,
      isWall: false,
      previousNode: null
    };
  };

  return (
    <main id="Pathfinder">
      {grid.map((row, rowIdx) => {
        return (
          <div key={rowIdx}>
            {row.map((node, nodeIdx) => {
              const { row, col, isFinish, isStart } = node;
              return (
                <Node
                  key={nodeIdx}
                  col={col}
                  isFinish={isFinish}
                  isStart={isStart}
                  row={row}
                ></Node>
              );
            })}
          </div>
        );
      })}
    </main>
  );
};

export default Pathfinder;

I'm basically making a grid of Nodes components; I have to use useEffect because I was trying to make using arrow functions only and hooks, and without classes / react components, which is why I can't use something like componentWillMount.

2
  • try putting the useeffect below your setGrid function Commented Jan 2, 2020 at 17:45
  • getGrid is a really confusing name for a function that calls setGrid. Perhaps setInitialGrid? Commented Jan 2, 2020 at 17:47

3 Answers 3

3

You need to pass the used objects, in your case the getGrid function to useEffect hook to remove the warning message.

Just like the following:

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

Additional suggestion from T.J. Crowder to use useCallback for getGrid. You can do that as the following:

const getGrid = useCallback(() => {
    setGrid(getInitGrid());
}, [getInitGrid]);

Read further here:

  1. useEffect hook
  2. useCallback hook

You need to do the same with getInitGrid in your code.

I hope this helps!

Sign up to request clarification or add additional context in comments.

3 Comments

getGrid should probably be wrapped in useCallback as well.
@T.J.Crowder That's right, just updated my answer, thanks.
No need to list setGrid as a dep on getGrid. State setter functions are guaranteed to be stable.
1

As TJ mentioned, moving the functions getInitGrid and createNode outside of the component is the best move. The way you have it right now, the functions will be redefined on every render and that isn't necessary as they are not directly tied to your components state.

Additionally, the useState hook can use a lazy intitial state which means you can pass a function for the initial state argument, and you wouldn't need the useEffect hook at all.

Here is what I ended up with:

import React, { useState } from "react";
import "../css/main.css";
import Node from "./node";

const START_NODE_ROW = 10;
const START_NODE_COL = 15;
const FINISH_NODE_ROW = 10;
const FINISH_NODE_COL = 35;

const createNode = (col, row) => {
  return {
    col,
    row,
    isStart: row === START_NODE_ROW && col === START_NODE_COL,
    isFinish: row === FINISH_NODE_ROW && col === FINISH_NODE_COL,
    distance: Infinity,
    isVisited: false,
    isWall: false,
    previousNode: null
  };
};

const getInitGrid = () => {
  const grid = [];
  for (let row = 0; row < 20; row++) {
    const currentRow = [];
    for (let col = 0; col < 50; col++) {
      currentRow.push(createNode(col, row));
    }
    grid.push(currentRow);
  }
  return grid;
};

const Pathfinder = () => {

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

  return (
    <main id="Pathfinder">
      {grid.map((row, rowIdx) => {
        return (
          <div key={rowIdx}>
            {row.map((node, nodeIdx) => {
              const { row, col, isFinish, isStart } = node;
              return (
                <Node
                  key={nodeIdx}
                  col={col}
                  isFinish={isFinish}
                  isStart={isStart}
                  row={row}
                ></Node>
              );
            })}
          </div>
        );
      })}
    </main>
  );
};

export default Pathfinder;

Comments

0

Most of the content within Pathfinder is static and should be moved outside it into its module; that will also have the effect of dealing with the useEffect problem you're having. All of the START_NODE_ROW etc. consts, getInitGrid, and createNode are static, there's no need to recreate them every time. I'd also wrap getGrid in useCallback and probably reorder things slightly so that things are only used after they're defined, but that's primarily for the human reader, not the compiler/JavaScript engine:

import React, { useEffect, useState } from "react";
import "../css/main.css";
import Node from "./node";

const START_NODE_ROW = 10;
const START_NODE_COL = 15;
const FINISH_NODE_ROW = 10;
const FINISH_NODE_COL = 35;

const getInitGrid = () => {
  const grid = [];
  for (let row = 0; row < 20; row++) {
    const currentRow = [];
    for (let col = 0; col < 50; col++) {
      currentRow.push(createNode(col, row));
    }
    grid.push(currentRow);
  }
  return grid;
};

const createNode = (col, row) => {
  return {
    col,
    row,
    isStart: row === START_NODE_ROW && col === START_NODE_COL,
    isFinish: row === FINISH_NODE_ROW && col === FINISH_NODE_COL,
    distance: Infinity,
    isVisited: false,
    isWall: false,
    previousNode: null
  };
};

const Pathfinder = () => {

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

  const getGrid = useCallback(() => {
    setGrid(getInitGrid());
  }, []);

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

  return (
    <main id="Pathfinder">
      {grid.map((row, rowIdx) => {
        return (
          <div key={rowIdx}>
            {row.map((node, nodeIdx) => {
              const { row, col, isFinish, isStart } = node;
              return (
                <Node
                  key={nodeIdx}
                  col={col}
                  isFinish={isFinish}
                  isStart={isStart}
                  row={row}
                ></Node>
              );
            })}
          </div>
        );
      })}
    </main>
  );
};

export default Pathfinder;

Separately, getGrid is tiny and used only once. For me, it's not worth breaking it out into its own function:

import React, { useEffect, useState } from "react";
import "../css/main.css";
import Node from "./node";

const START_NODE_ROW = 10;
const START_NODE_COL = 15;
const FINISH_NODE_ROW = 10;
const FINISH_NODE_COL = 35;

const getInitGrid = () => {
  const grid = [];
  for (let row = 0; row < 20; row++) {
    const currentRow = [];
    for (let col = 0; col < 50; col++) {
      currentRow.push(createNode(col, row));
    }
    grid.push(currentRow);
  }
  return grid;
};

const createNode = (col, row) => {
  return {
    col,
    row,
    isStart: row === START_NODE_ROW && col === START_NODE_COL,
    isFinish: row === FINISH_NODE_ROW && col === FINISH_NODE_COL,
    distance: Infinity,
    isVisited: false,
    isWall: false,
    previousNode: null
  };
};

const Pathfinder = () => {

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

  useEffect(() => {
    setGrid(getInitGrid());
  }, []);

  return (
    <main id="Pathfinder">
      {grid.map((row, rowIdx) => {
        return (
          <div key={rowIdx}>
            {row.map((node, nodeIdx) => {
              const { row, col, isFinish, isStart } = node;
              return (
                <Node
                  key={nodeIdx}
                  col={col}
                  isFinish={isFinish}
                  isStart={isStart}
                  row={row}
                ></Node>
              );
            })}
          </div>
        );
      })}
    </main>
  );
};

export default Pathfinder;

(You don't have to declare setGrid or getInitGrid as dependencies, since the first is a state setter function from React that is guaranteed to be stable, and the second is defined outside Pathfinder.)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.