为什么 useState 不触发重新渲染?
Why is useState not triggering re-render?
我已经初始化了一个数组状态,当我更新它时,我的组件不会重新呈现。这是一个最小的概念验证:
function App() {
const [numbers, setNumbers] = React.useState([0, 1, 2, 3]);
console.log("rendering...");
return (
<div className="App">
{numbers.map(number => (
<p>{number}</p>
))}
<input
type="text"
value={numbers[0].toString()}
onChange={newText => {
let old = numbers;
old[0] = 1;
setNumbers(old);
}}
/>
</div>
);
}
根据这段代码,似乎输入应该包含数字 0 开始,并且任何时候改变,状态也应该改变。在input中输入“02”后,App组件并没有重新渲染。但是,如果我在5秒后执行的onChange函数中添加一个setTimeout,它表明数字确实已经更新了。
关于组件不更新的原因有什么想法吗?
这是一个CodeSandbox概念验证。
你需要像这样复制numbers
let old = [...numbers];
useState
仅在值发生变化时才更新该值,因此如果它是 44
并且变为 7
它将更新。但是它怎么知道数组或对象是否发生了变化。它是通过引用所以当你做 let old = numbers
你只是传递一个引用而不是创建一个新的
您正在调用 setNumbers
并将其传递给它已有的数组。你已经改变了它的一个值,但它仍然是同一个数组,我怀疑 React 没有看到 re-render 的任何理由,因为状态没有改变;新数组是旧数组。
避免这种情况的一种简单方法是将数组分散到一个新数组中:
setNumbers([...old])
其他人已经给出了技术方案。对于任何对为什么会发生这种情况感到困惑的人,是因为 setSomething() 仅当且仅当先前状态和当前状态不同时才重新渲染组件。由于javascript中的数组是引用类型,如果在js中编辑数组的某一项,仍然不会改变对原数组的引用。在js眼里,这两个数组是一样的,尽管这两个数组里面原来的内容是不一样的。这就是为什么 setSomething() 无法检测到对旧数组所做的更改。
请注意,如果您使用 class 组件并使用 setState() 更新状态,那么无论状态是否发生变化,组件都会始终更新。因此,您可以将功能组件更改为 class 组件作为解决方案。或者按照其他人提供的答案。
useState 是一个 React 钩子,它提供了在功能组件中拥有 State 的功能。
通常它会通知 React 在 useState 变量发生变化时重新渲染组件。
{
let old = numbers;
old[0] = 1;
setNumbers(old);
}
在上面的代码中,由于您指的是同一个变量,它存储的是引用而不是值,因此 React 不知道最新的更改,因为引用与以前的相同。
为了克服使用下面的 hack,它不会复制引用,而是深度复制(复制值)
{
let old = JSON.parse(JSON.stringify(numbers));
old[0] = 1;
setNumbers(old);
}
编码愉快:)
你可以这样改变状态
const [state, setState] = ({})
setState({...state})
或者如果你的状态是数组,你可以这样改变
const [state, setState] = ([])
setState([...state])
//define state using useState hook
const [numbers, setNumbers] = React.useState([0, 1, 2, 3]);
//copy existing numbers in temp
let tempNumbers = [...numbers];
// modify/add no
tempNumbers.push(4);
tempNumbers[0] = 10;
// set modified numbers
setNumbers(tempNumbers);
我已经初始化了一个数组状态,当我更新它时,我的组件不会重新呈现。这是一个最小的概念验证:
function App() {
const [numbers, setNumbers] = React.useState([0, 1, 2, 3]);
console.log("rendering...");
return (
<div className="App">
{numbers.map(number => (
<p>{number}</p>
))}
<input
type="text"
value={numbers[0].toString()}
onChange={newText => {
let old = numbers;
old[0] = 1;
setNumbers(old);
}}
/>
</div>
);
}
根据这段代码,似乎输入应该包含数字 0 开始,并且任何时候改变,状态也应该改变。在input中输入“02”后,App组件并没有重新渲染。但是,如果我在5秒后执行的onChange函数中添加一个setTimeout,它表明数字确实已经更新了。
关于组件不更新的原因有什么想法吗?
这是一个CodeSandbox概念验证。
你需要像这样复制numbers
let old = [...numbers];
useState
仅在值发生变化时才更新该值,因此如果它是 44
并且变为 7
它将更新。但是它怎么知道数组或对象是否发生了变化。它是通过引用所以当你做 let old = numbers
你只是传递一个引用而不是创建一个新的
您正在调用 setNumbers
并将其传递给它已有的数组。你已经改变了它的一个值,但它仍然是同一个数组,我怀疑 React 没有看到 re-render 的任何理由,因为状态没有改变;新数组是旧数组。
避免这种情况的一种简单方法是将数组分散到一个新数组中:
setNumbers([...old])
其他人已经给出了技术方案。对于任何对为什么会发生这种情况感到困惑的人,是因为 setSomething() 仅当且仅当先前状态和当前状态不同时才重新渲染组件。由于javascript中的数组是引用类型,如果在js中编辑数组的某一项,仍然不会改变对原数组的引用。在js眼里,这两个数组是一样的,尽管这两个数组里面原来的内容是不一样的。这就是为什么 setSomething() 无法检测到对旧数组所做的更改。
请注意,如果您使用 class 组件并使用 setState() 更新状态,那么无论状态是否发生变化,组件都会始终更新。因此,您可以将功能组件更改为 class 组件作为解决方案。或者按照其他人提供的答案。
useState 是一个 React 钩子,它提供了在功能组件中拥有 State 的功能。 通常它会通知 React 在 useState 变量发生变化时重新渲染组件。
{
let old = numbers;
old[0] = 1;
setNumbers(old);
}
在上面的代码中,由于您指的是同一个变量,它存储的是引用而不是值,因此 React 不知道最新的更改,因为引用与以前的相同。
为了克服使用下面的 hack,它不会复制引用,而是深度复制(复制值)
{
let old = JSON.parse(JSON.stringify(numbers));
old[0] = 1;
setNumbers(old);
}
编码愉快:)
你可以这样改变状态
const [state, setState] = ({})
setState({...state})
或者如果你的状态是数组,你可以这样改变
const [state, setState] = ([])
setState([...state])
//define state using useState hook
const [numbers, setNumbers] = React.useState([0, 1, 2, 3]);
//copy existing numbers in temp
let tempNumbers = [...numbers];
// modify/add no
tempNumbers.push(4);
tempNumbers[0] = 10;
// set modified numbers
setNumbers(tempNumbers);