Javascript 渲染后记录状态的结果相同
我知道useState是异步的。回答前请阅读完整问题。 我试图用Javascript 渲染后记录状态的结果相同,javascript,reactjs,react-hooks,setstate,Javascript,Reactjs,React Hooks,Setstate,我知道useState是异步的。回答前请阅读完整问题。 我试图用useState修改数组中的一个元素,但它没有按预期工作 数组及其修改函数: const [table, setTable] = useState(['blue', 'blue', 'blue', 'blue', 'blue']); let currentShapePosition = 2; function printTable() { let newTable = [...table]; // let newTabl
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调用是异步的,不会直接更新。 参考:
阅读此答案中的更多内容:问题是您的第一个
useffect
,因为您删除了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
(请参阅),其中所有数组的值均为“蓝色”,并将一直保持到卸载为止
您应该知道,在每个渲染上都执行组件体,因此在您的情况下,每个函数都有一个新实例
这同样适用于应作为参考的currentPosition
若要修复此问题,请删除eslint
注释并使用警告:
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]);
使用React方法创建的变量是具有特殊行为的特殊对象;)强> 函数引用变量
表
,它是一个特殊的React(“state”)对象。看起来,在这种情况下,函数总是获取变量的初始状态-否则它至少会在下一次渲染时显示不同的值
如果使用常规表作为变量,您将得到预期结果。我认为您的代码的这个更改版本很好地证明了这一点:
import React,{useState,useffect}来自“React”;
从“react dom”导入react dom;
导入“/style.css”;
常量应用=()=>{
设currentShapePosition=2;
让tableDemo=[“蓝色”、“蓝色”、“蓝色”、“蓝色”、“蓝色”];
函数setTableDemo(newTable){
tableDemo=newTable.slice();
可设置(tableDemo);//用于触发渲染
}
//用于渲染目的(保留原始代码结构)
const[table,setTable]=使用状态(tableDemo);
useffect(()=>{
printTable();
window.addEventListener(“向下键”,handleKeyPress);
//eslint禁用下一行react HOOK/deps
}, []);
useffect(()=>{
console.log(“呈现”,表);
});
函数printTable(){
设newTable=[…表];
//设newTable=table;
新表[currentShapePosition]=“红色”;
setTableDemo(新表);
console.log(“printTable newTable”,newTable);//{
常数[currentPosition,setPosition]=使用状态(2);
const table=useRef([“蓝色”、“蓝色”、“红色”、“蓝色”、“蓝色”);
功能手柄按键(事件){
让我们重新定位;
控制台日志(“当前位置:”,当前位置);
表.当前[currentPosition]=“蓝色”;
开关(事件键){
案例“左”://IE/边缘特定值
案例“箭头左”:
newPosition=当前位置-1;
打破
案例“右”://IE/边缘特定值
案例“ArrowRight”:
新位置=当前位置+1;
打破
违约:
}
表.当前[新位置]=“红色”;
console.log(“newPosition:”,newPosition);
console.log(“表:”,表.current);
//触发新渲染
设置位置(新位置);
}
useffect(()=>{
window.addEventListener(“向下键”,handleKeyPress);
return()=>window.removeEventListener(“keydown”,handleKeyPress);
});
返回(
);
};
const root=document.getElementById(“根”);
ReactDOM.render(,root);
当然,最好的解决方案是使用相关类动态呈现
元素,而不使用任何表。但这是另一回事,超越了这个问题。我确实理解useState是异步的,因此数组可能不会立即更改,但是,在printTable函数中,即使在服务器关闭后,console.log结果也是相同的al重新呈现。事实上,即使在组件更新之后,例如使用useEffect,在打印表函数中,console.log结果是相同的。请阅读问题,我投了反对票,因为它没有回答问题。可能重复的不是重复的,人们请阅读问题如果它不是重复的,它是缺失的…它有一个例子在代码沙盒中,我相信这是由于闭包造成的,但我承认我不是100%赞同它。我在控制台中看到这些值:printTable newTable[“蓝”、“蓝”、“红”、“蓝”、“蓝”]printTable[“蓝”、“蓝”、“蓝”、“蓝”、“蓝”]render[“蓝”、“蓝”、“蓝”、“红”、“蓝”]我认为在第二种情况下,它不应该总是“蓝色”.可能是的。在问我的问题之前,我读了一些关于闭包的书,但闭包的工作方式目前我还不是很清楚。我发现了一个bug,它被认为是一个bug,如果答案有用的话,请随意接受。现在唯一缺少的是useffect
hook的返回值,它应该是一个删除e键事件。return()=>window.removeEventListener(“keydown”,handleKeyPress);
谢谢,出于同样的原因,还应该有一个currentPosition
的参考
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]);
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);