Переменные, созданные с помощью методов React, являются специальными объектами со специальным поведением;)
Ваша функция ссылается на переменную table
, которая является специальным объектом React ("состояние"). Похоже, в таком случае функция всегда получает начальное состояние переменной - в противном случае она будет показывать другие значения, по крайней мере, при следующих визуализациях.
Если вы используете обычную таблицу в качестве переменной, вы получите то, чтоожидается. Я думаю, что эта измененная версия вашего кода демонстрирует это довольно хорошо:
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import "./style.css";
const App = () => {
let currentShapePosition = 2;
let tableDemo = ["blue", "blue", "blue", "blue", "blue"];
function setTableDemo(newTable) {
tableDemo = newTable.slice();
setTable(tableDemo); // used to trigger rendering
}
// for rendering purposes (to keep the original code structure)
const [table, setTable] = useState(tableDemo);
useEffect(() => {
printTable();
window.addEventListener("keydown", handleKeyPress);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
console.log("render", table);
});
function printTable() {
let newTable = [...table];
// let newTable = table;
newTable[currentShapePosition] = "red";
setTableDemo(newTable);
console.log("printTable newTable", newTable); // <-- the result is as expected
console.log("printTable tableDemo", tableDemo); // <--- The problem is here. I don't get why the initial value never change
}
function handleKeyPress(event) {
switch (event.key) {
case "Left": // IE/Edge specific value
case "ArrowLeft":
moveShape(-1);
break;
case "Right": // IE/Edge specific value
case "ArrowRight":
moveShape(1);
break;
default:
return;
}
}
function moveShape(direction) {
currentShapePosition += direction;
printTable();
}
return (
<table className="tetris-table">
<tbody>
<tr>
<td className={table[0]} />
<td className={table[1]} />
<td className={table[2]} />
<td className={table[3]} />
<td className={table[4]} />
</tr>
</tbody>
</table>
);
};
const root = document.getElementById("root");
ReactDOM.render(<App />, root);
Используйте объекты React "state" только для сохранения "значений состояния источника"
По сути, лучше использовать объекты React «state» только для того, чтобы сохранить «значения состояния источника», используемые для запуска повторного рендеринга. В этом конкретном случае значение состояния источника currentShapePosition
. Таблица как таковая не должна изменяться - только некоторые конкретные элементы этой таблицы. Таким образом, на самом деле код, согласно подходу React, может выглядеть следующим образом:
import React, { useState, useEffect, useRef } from "react";
import ReactDOM from "react-dom";
import "./style.css";
const App = () => {
const [currentPosition, setPosition] = useState(2);
const table = useRef(["blue", "blue", "red", "blue", "blue"]);
function handleKeyPress(event) {
let newPosition;
console.log("currentPosition:", currentPosition);
table.current[currentPosition] = "blue";
switch (event.key) {
case "Left": // IE/Edge specific value
case "ArrowLeft":
newPosition = currentPosition - 1;
break;
case "Right": // IE/Edge specific value
case "ArrowRight":
newPosition = currentPosition + 1;
break;
default:
}
table.current[newPosition] = "red";
console.log("newPosition:", newPosition);
console.log("table:", table.current);
// trigger the new render
setPosition(newPosition);
}
useEffect(() => {
window.addEventListener("keydown", handleKeyPress);
return () => window.removeEventListener("keydown", handleKeyPress);
});
return (
<table className="tetris-table">
<tbody>
<tr>
<td className={table.current[0]} />
<td className={table.current[1]} />
<td className={table.current[2]} />
<td className={table.current[3]} />
<td className={table.current[4]} />
</tr>
</tbody>
</table>
);
};
const root = document.getElementById("root");
ReactDOM.render(<App />, root);
Конечно, лучшее решение - это динамическая визуализация элементов <td>
с соответствующим классом без какой-либо таблицы. ,Но за этим вопросом стоит другая история.