我了解useState是异步的。请在回答之前阅读完整的问题。
我正在尝试使用useState
修改数组中的元素,但是它没有按预期工作。
数组及其修改函数:
const [table, setTable] = useState(['blue', 'blue', 'blue', 'blue', 'blue']);
let currentShapePosition = 2;
function printTable() {
let newTable = [...table];
// let newTable = table;
newTable[currentShapePosition] = 'red';
setTable(newTable);
console.log('printTable newTable', newTable); // <-- the result is as expected
// log => printTable newTable ["blue", "blue", "red", "blue", "blue"]
console.log('printTable table', table); // <--- The problem is here. I don't get why the array never update
// log => printTable newTable ["blue", "blue", "blue", "blue", "blue"]
}
因为
useState
是异步的,所以我知道数组可能不会立即更改,但是在printTable
函数内部,即使多次重新提交,console.log
的结果也是相同的。什么时候代替:
let newTable = [...table]
我这样做:
let newTable = table
然后,在函数中生成的console.log中更新状态,但是没有重新渲染/组件更新。
我想理解为什么在多次重新渲染之后,console.log函数内部的
newTable = [...table]
在第一种情况下是。以及为什么在第二种情况下
newTable = table
中,尽管使用setTable(newTable)
却没有重新呈现组件。import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import "./style.css";
const App = () => {
let currentShapePosition = 2;
const [table, setTable] = useState(["blue", "blue", "blue", "blue", "blue"]);
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";
setTable(newTable);
console.log("printTable newTable", newTable); // <-- the result is as expected
console.log("printTable table", table); // <--- 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);
最佳答案
问题是您的第一个useEffect
,因为您删除了eslint
警告,它隐藏了潜在的错误。
useEffect(() => {
printTable();
window.addEventListener('keydown', handleKeyPress);
// v hidden bug, you should consider the warnings
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
此处发生的是,在组件上安装了,这是分配给
handleKeyPress
的addEventListener
的closures
的的第一个实例(请参阅 currentPosition
),其中所有数组的值均为“blue”,并且它将一直保持这种状态直到卸载。您应该知道,在执行组件主体的每个渲染上,因此,在您的情况下,每个函数都有一个新实例。
应该作为引用的
eslint
也是如此。要解决此问题,请删除ojit_code注释并附带警告:
useEffect(() => {
const printTable = () => {
let newTable = [...table];
newTable[currentPosition.current] = 'red';
setTable(newTable);
console.log('closure', table);
};
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) {
currentPosition.current += direction;
printTable();
}
window.addEventListener('keydown', handleKeyPress);
return () => window.removeEventListener('keydown', handleKeyPress);
}, [table]);
关于javascript - 渲染后记录状态的结果相同,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/58329134/