我知道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
然后在控制台内更新状态。在函数中生成日志,但没有重新渲染/组件更新。
我想了解为什么在第一种情况下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);
useState调用是异步的,它们不直接更新。参考:https://reactjs.org/docs/hooks-state.html
阅读此答案中的更多内容:useState set方法不会立即反映更改
使用React方法创建的变量是具有特殊行为的特殊对象;(
函数引用变量表
,它是一个特殊的React(“state”)对象。看起来,在这种情况下,函数总是获取变量的初始状态-否则它至少会在下一次渲染时显示不同的值。
如果使用正则表作为变量,您将得到预期的结果。我认为代码的这个修改版本很好地证明了这一点:
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”对象来保持用于触发重新渲染的“源状态值”。在此特定情况下,源状态值为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);
当然,最好的解决方案是动态渲染
问题是您的第一个useffect
,因为您删除了eslint
警告,它隐藏了一个潜在的bug。
useEffect(() => {
printTable();
window.addEventListener('keydown', handleKeyPress);
// v hidden bug, you should consider the warnings
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
此处发生的情况是,在组件装载上,分配给addEventListener
(请参阅Closes
)的handleKeyPress
的第一个实例,其中所有数组的值均为“蓝色”,并将一直保持该状态,直到卸载。
您应该知道,在执行的每个渲染组件体上,因此在您的情况下,每个函数都有一个新实例。
这同样适用于应作为参考的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不会重新渲染组件。无状态组件也是如此吗?还是它们的行为更像“愚蠢的”函数,并且每次都会执行? 例如,如果我有: 当更新其价值,并获取重新渲染?还是像其他React组件一样足够“聪明”地看到其道具没有改变? 问题答案: 更新25.10.2018 从React 16.6开始,您可以将React.memo用于功能组件以防止重新
我有一个带有两组按钮(功能和目标)的界面。当我点击一个按钮时,我希望它从一个团队转到另一个团队。我正在用react和redux实现这一点。我唯一的问题是,当状态更新并且我成功地记录了更新的状态时,除非我使用forceUpdate(),否则组件不会更新。我不明白的是,既然状态更新成功,组件不应该自动重新渲染吗? 一些js 正如您所看到的,当我单击一个按钮时,我触发updateLists函数,该函数将
问题内容: 渲染模板后如何运行方法?我要设置和之后,我需要使用JQuery进行更改(例如,在模板内容的DOM中)。正在“之前”渲染工作(模板的DOM尚不可用)。谢谢。 问题答案: 创建在链接函数中运行代码的指令。构建模板后,将调用链接功能。 请参阅ng-click以获取想法。
首先,我尝试检查用户的id是否与DB中的ID匹配。如果是这样,那么为和设置一个新状态,但我收到以下警告
我有一个由WordPress预先呈现的HTML页面和三个React组件:
有一个案例我很困惑。 我有一个对象redux状态:{show:true,creative:{name:'a',tags:[t1,t2,t3]}。此对象已通过“react redux”的“connect”功能映射到道具。 当我改变属性“显示”或“creative.name”时,它确实会触发重新渲染。但是如果我将t0追加到"creative.tags",它就不会运行re-渲染。 我必须分配一个新变量来