Have you ever gotten the enigmatic Next.js error: "Warning: Prop "{your-prop}" did not match. Server: "{value-1}" Client: "{value-2}"?
Here is the fun fact, conditional rendering did not cause this error. But at the beginning I thought it did, that's why I created this post, so everyone struggling to get rid of that error can understand why it happens, and how to solve it.
Here is our example:
type TGrid = (0 | 1)[][];
const generateRandomTiles = () => {
const grid: TGrid = [];
for (let i = 0; i < numRows; i++) {
grid.push(Array.from(Array(numCols), () => (Math.random() > 0.7 ? 1 : 0))); // returns a live cell 70% of the time
}
return grid;
};
export const Game = () => {
const [grid, setGrid] = useState<TGrid>(generateRandomTiles());
return (
grid?.map((rows, i) =>
rows.map((col, k) => {
return (
<div
// classNames is a library that combines class texts. But this can be thought as "w-4 h-4 border-solid border border-gray-400" + grid[i][k] ? "bg-pink-400" : "bg-white"
className={classNames(
"w-4 h-4 border-solid border border-gray-400",
grid[i][k] ? "bg-pink-400" : "bg-white"
)}
key={`${i}-${k}`}
/>
)
})
)
);
};
This code causes the following error to fire:
Warning: Prop
className
did not match. Server: "w-4 h-4 border-solid border border-gray-400 bg-white" Client: "w-4 h-4 border-solid border border-gray-400 bg-pink-400"
So I thought it happened because I conditionally rendered bg-pink-400
and bg-white
. But it wasn't, the real problem is a little more sneaky than that.
Do not generate values when defining useState.
The real culprit is this the line where we define our grid state:
const [grid, setGrid] = useState<TGrid>(generateRandomTiles());
The problem here is that Next.js has certain state in mind when we generate the tiles in the server, but when the client runs hydrates, we end up with a different random array when we generateRandomTiles() in the frontend. You can notice this if you console log grid, and check your next.js terminal log and your frontend log.
There is a great article by react.gg about this: https://ui.dev/c/react/effects?ck_subscriber_id=2248853627
How do we fix it?
Simple, we manage effects on the frontend. Like this:
const [grid, setGrid] = useState<TGrid>();
useEffect(() => {
const initialGrid = generateRandomTiles();
// Comment, first refresh
setGrid(initialGrid);
}, []);
Now we will not have a mismatch between frontend and backend style rendering, and the error will not happen anymore.
Top comments (1)
Thanks bro, this was really helpful