Reputation: 59
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
.
Upvotes: 2
Views: 95
Reputation: 1927
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;
Upvotes: 1
Reputation: 15166
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:
You need to do the same with getInitGrid
in your code.
I hope this helps!
Upvotes: 3
Reputation: 1075337
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
.)
Upvotes: 0