我对道具如何与钩子创建的状态进行交互有疑问。
在下面的示例中,钩子中保持的状态始终落后于道具中定义的状态。
我思考我理解为什么会这样。生命周期似乎是这样的:
handleClick
定义为可基于此渲染器访问范围handleClick
handleClick
仍然只能访问渲染中定义的范围,因此items
仍为空add
被调用;父组件中的状态已更新,但是setHookItems
将使用在定义handleClick
时在props中定义的状态hookItems
设置为items
,它为空这是第一次渲染,但我认为该原理适用于所有其他渲染;单击处理程序只能访问添加新项目之前的 范围,因此,根据其作为items
的状态来设置挂钩状态。
我对这种情况如何发生的假设可能是错误的,但似乎与我所见一致。
因此,对于实际问题;如何基于某种处理程序(而不是上次渲染时的道具)基于最新道具更新处于挂钩状态的项目?
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>
答案 0 :(得分:1)
问题在于,每次渲染组件时,它都会获得一组状态和属性值-但是这些值直到下一次渲染时才改变。
所以,如果我有这样的状态:
const [count, setCount] = useState(0);
如果我执行setCount(count + 1)
,它将在count
变量为1
的位置进行新的渲染,但不会立即导致count
变量变为1
。 (不能-const
变量无法更改)因此,如果我在设置后立即登录count
,它仍然为零。
在您的示例中,存在一个间接级别,即setter在父组件中,由add
函数触发,并且该值作为prop传递给子级,但这不会改变行为。
在设计方面,这里的问题是您使用的是“道具陈述”模式,通常是一种反模式。通常,您的组件不应将道具保存为状态,而应直接使用道具。否则,您会遇到道具和状态不同步的问题。
react博客上有一篇文章You Probably Don't Need Derived State,其中对此进行了更详细的介绍。