挂钩状态落后于道具状态

时间:2019-11-07 16:06:02

标签: javascript reactjs react-hooks

我对道具如何与钩子创建的状态进行交互有疑问。

在下面的示例中,钩子中保持的状态始终落后于道具中定义的状态。

思考我理解为什么会这样。生命周期似乎是这样的:

  1. 组件渲染
  2. handleClick定义为可基于此渲染器访问范围
  3. 单击按钮并调用handleClick
  4. handleClick仍然只能访问渲染中定义的范围,因此items仍为空
  5. add被调用;父组件中的状态已更新,但是setHookItems将使用在定义handleClick时在props中定义的状态
  6. 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>

1 个答案:

答案 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,其中对此进行了更详细的介绍。