为什么useState不触发重新渲染?

我已经初始化了一个数组状态,当我更新它时,我的组件不会重新呈现。下面是一个最小的概念证明:

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来开始,并且任何时候它被改变,状态也应该改变。输入“02”后;在输入中,App组件不会重新渲染。然而,如果我在onChange函数中添加了一个setTimeout,它在5秒后执行,它显示数字确实已经更新。

对于组件不更新的原因有什么想法吗?

下面是一个概念证明的CodeSandbox

134563 次浏览

你需要像这样复制numbers

useState仅在值发生变化时才更新值,因此如果它是44,它变成了7,它将更新。但它如何知道数组或对象是否发生了变化。它是通过引用的,所以当你执行let old = numbers时,你只是传递一个引用,而不是创建一个新的引用

你正在调用setNumbers并将它已经拥有的数组传递给它。你已经改变了它的一个值,但它仍然是相同的数组,我怀疑React没有看到任何理由重新渲染,因为状态没有改变;新数组就是旧数组。

避免这种情况的一个简单方法是将数组展开到一个新数组中:

setNumbers([...old])

其他人已经给出了技术解决方案。对于那些不知道为什么会发生这种情况的人来说,是因为setSomething()只在且仅当之前的状态和当前状态不同时才重新呈现组件。由于javascript中的数组是引用类型,如果你在js中编辑数组的一个项,它仍然不会改变对原始数组的引用。在js的眼中,这两个数组是相同的,即使这些数组中的原始内容是不同的。这就是为什么setSomething()无法检测到对旧数组所做的更改。

注意,如果使用类组件并使用setState()更新状态,那么无论状态是否改变,组件总是会更新。因此,您可以将功能组件更改为类组件作为解决方案。或者按照别人提供的答案去做。

useState是一个React钩子,它提供了在函数组件中拥有State的功能。 通常,当useState变量发生变化时,它会通知React重新呈现组件

{
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})

或者如果你的状态是Array,你可以这样改变

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);

我正在处理一个数组,其中有对象,我尝试了上面的几个。

我的useState是:

const [options, setOptions] = useState([
{ sno: "1", text: "" },
{ sno: "2", text: "" },
{ sno: "3", text: "" },
{ sno: "4", text: "" },
]);

现在我想在点击button时添加更多空白字段的选项,我会使用以下方式来实现我的目的:

<button
onClick={() => {
setOptions([...options, { sno: newArray.length + 1, text: "" }]);
}}
>

这解决了我的问题,我能够重新渲染组件,并添加一个对象到数组。

我对此没有任何骄傲,但它确实有效

anotherList = something
setSomething([])
setTimeout(()=>{ setSomething(anotherList)},0)

引入组件的一个数组,该数组不是钩子的数组。例如:

const [numbers, setNumbers] = useState([0, 1, 2, 3]);


var numbersModify = []; //the value you want

最后:

setNumbers(numbersModify)

修改这个numbersModify,当钩子刷新时,它将返回0 numbersModify,钩子将保持这个状态。因此,看不到更改的问题将被消除。

:D