我对 Prop 如何与钩子(Hook)创建的状态进行交互有疑问。
在下面的示例中,钩子(Hook)中保存的状态始终落后于属性中定义的状态。
我认为我明白为什么会发生这种情况。生命周期似乎是这样的:
- 组件渲染
handleClick
定义为可以访问基于此渲染的范围- 按钮被点击并调用
handleClick
handleClick
仍然只能访问渲染中定义的范围,因此,items
仍然为空add
被调用;父组件中的状态已更新,但setHookItems
将使用定义handleClick
时在 props 中定义的状态hookItems
设置为items
,它是空的
这是第一次渲染,但我认为该原则适用于所有其他渲染;单击处理程序只能访问添加新项目之前的范围,因此,根据其具有的项目状态来设置 Hook 状态。
我对事情如何发生的假设可能是错误的,但它似乎与我所看到的一致。
那么,回到实际问题;如何根据某种处理程序的最新 Prop (而不是上次渲染时的 Prop )更新处于 Hook 状态的项目?
const { useState } = React;
const App = ({items, add}) => {
const [hookItems, setHookItems] = React.useState([]);
const handleClick = () => {
add(Math.floor(Math.random() * 10));
setHookItems(items);
}
return (
<div>
<p><span class="title">All Items: </span>{items.map(x => (<span>{x}</span>))}</p>
<p><span class="title">Hook Items: </span>{hookItems.map(x => (<span>{x}</span>))}</p>
<button onClick={handleClick}>Add</button>
</div>
);
}
const Root = () => {
const [items, setItems] = useState([]);
return (
<div>
<App items={items} add={item => setItems([...items, item])} />
</div>
)
}
ReactDOM.render(
<Root />,
document.getElementById('app')
);
.title { display: inline-block; width: 100px; text-align: right; margin-right: 20px;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id="app"></div>
最佳答案
问题是,每次渲染组件时,它都会获取其状态和 Prop 的一组值 - 但这些值直到下一次渲染才会更改。
所以如果我有这样的状态:
const [count, setCount] = useState(0);
如果我执行setCount(count + 1)
,它会排队一个新的渲染,其中count
变量将为1
,但它不会立即导致 count
变量变为 1
。 (它不能 - const
变量无法更改)因此,如果我在设置后立即记录count
,它仍然为零.
在您的示例中,存在一定程度的间接性,其中 setter 位于父组件中,由 add
函数触发,并且该值作为 prop 传递给子组件,但这并不'不要改变行为。
就设计而言,这里的问题是您正在使用“props to state”模式,这通常是一种反模式。你的组件通常不应该将 props 保存为状态,你应该直接使用 props。否则你会遇到 props 和 state 不同步的问题。
React 博客有一篇文章 You Probably Don't Need Derived State其中更详细地介绍了这一点。
关于javascript - Hook 状态落后于 prop 状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58752699/