#javascript #reactjs #state #rendering #memo
Вопрос:
Я недавно начал изучать React и создаю игру 2048 года. Теперь всякий раз, когда пользователь выполняет перемещение, я вношу определенные изменения в сетку, которая, следовательно, обновляет состояние. Однако при каждом последующем нажатии клавиши количество повторных отображений сетки увеличивается экспоненциально и зависает после определенной точки, даже если состояние (значение сетки) не меняется. Проблема сохраняется даже после использования React.memo. Как мне устранить эти нежелательные повторные визуализации и почему это происходит?
import React, { useState, useEffect } from "react";
import Cell from "./Cell";
import "./Game.css";
let permittedKeys = [
"ArrowUp",
"KeyW",
"ArrowRight",
"KeyD",
"ArrowDown",
"KeyS",
"ArrowLeft",
"KeyA",
];
const boardSize = 4;
function Game() {
const [grid, setGrid] = useState();
console.log("re-rendering grid", grid);
//creating a 4x4 board when component mounts
useEffect(() => {
const board = getBoard(boardSize);
setGrid(board);
}, []);
//function that renders all the 4x4 cells
// CORRECTION - Give every cell a key
const renderCells =
grid amp;amp;
grid.map((row, i) => row.map((col, j) => <Cell key={i*boardSize j} value={grid[i][j]} />));
//check if a key is pressed
document.addEventListener("keydown", (e) => {
//If pressed key is not a valid game control key ignore the keydown event
if (permittedKeys.indexOf(e.code) === -1) return;
//Else
if (grid) setGrid(performMove(e.code, [...grid], boardSize));
});
return <div className="board">{renderCells}</div>;
}
const performMove = (pressedKey, board, boardSize) => {
//Move cells in pressedKey-direction till they hit another block
//Eg:When left key is pressed
//256 empty 256 64 becomes
//256 256 64 empty
for (let i = 0; i < boardSize; i) {
const newRow = [];
let count = 0;
for (let j = 0; j < boardSize; j)
if (board[i][j]) {
newRow.push(board[i][j]);
count;
}
while (count !== boardSize) {
count;
if (pressedKey === permittedKeys[6] || pressedKey === permittedKeys[7])
newRow.push(0);
else if (
pressedKey === permittedKeys[2] ||
pressedKey === permittedKeys[3]
)
newRow.unshift(0);
}
board[i] = newRow;
}
return board;
};
//generate the next cell after the player performs a move
function generateNext() {
return Math.random() < 0.9 ? 2 : 4;
}
function getBoard(boardSize) {
const board = [];
for (let i = 0; i < boardSize; i) {
const row = [];
for (let j = 0; j < 4; j) row.push(0);
board.push(row);
}
//generating two random starting cells
let i1 = Math.floor(Math.random() * boardSize);
let j1 = Math.floor(Math.random() * boardSize);
let i2, j2;
do {
i2 = Math.floor(Math.random() * boardSize);
j2 = Math.floor(Math.random() * boardSize);
} while (i1 === i2 amp;amp; j1 === j2);
board[i1][j1] = generateNext();
board[i2][j2] = generateNext();
return board;
}
export default React.memo(Game);
Комментарии:
1. При каждом рендеринге вы добавляете нового
keydown
слушателя, поэтому при каждом рендеринге вы получаете экспоненциально большеsetGrid
вызовов. Вы не должны добавлять слушателей напрямую, а скорее должны использовать цикл событий/рендеринга Reacts.2. @pilchard, это сработало для меня. Я добавил прослушиватель событий, когда компонент был смонтирован, вместо того, чтобы делать это в функциональном компоненте, и это решило мою проблему. Спасибо!