【问题标题】:How do I update an array using useState?如何使用 useState 更新数组?
【发布时间】:2020-10-28 20:38:06
【问题描述】:

我有以下场景:

DisplayList 组件,它呈现一个列表。列表记录通过 props 传入(这包含列表名称、描述、作者等),以及要列出的初始项目,以及当前的“模式”(可以是 'r'、'g' 或 'a' )。该组件显示列表信息,然后为每个列表项呈现一个DisplayItem 组件。 DisplayItem 组件还带有一个回调函数,这样当用户点击列表中的一个项目时,它就会被当前模式“标记”(即项目的“模式”属性发生变化)。

const DisplayList = (props) => {

  // Get list object (contains list name etc)
  // and current mode (r, a or g)
  const { list, currentMode } = props
  
  // Array of items to show
  // This is an array of objects, with each object representing a single item
  // eg {name: itemname, mode: r}
  const [ items,  setItems] = useState(props.items)

  // Callback function - triggered when the rendered DisplayItem is clicked
  // This changes the 'mode' of the item to the current mode
  function handleItemClick(index) {
      items[index].mode = currentMode
      setItems(items) // At this point the list of items should re-render
    }
  }

  return (
    <div className="displayItem">
      {
        items.map(item => <DisplayItem key={uuid()} item={item} handleCellClick={handleItemClick} />)
      }
    </div>
  )

}

我遇到的问题是单击一个项目似乎会触发 handleItemClick 函数来更新 itemList,但是当它到达 setItems 时,它不会重新渲染。

【问题讨论】:

  • 不是一个直接的答案,但是每次渲染生成一个 uuid 用作键是没有意义的。如果您没有唯一标识符来重用每个渲染,请使用数组索引。
  • 更直接的答案:items[index].mode = currentMode 改变状态并将原始数组引用传递给更新函数,这意味着无论您更新多少嵌套值,react 都不会将其视为更改。在变异之前创建一个新的状态副本,然后给更新函数提供 new 数组。
  • Brian 是正确的,您只需要创建一个新数组即可。但是您应该使用 uuid,使用数组索引是一种不好的做法。它会减慢您的应用程序并导致问题,因为您不能保证一个项目将始终具有相同的索引。最佳实践是给每个项目一个唯一的 id 并使用它,但 uuid 比 index.html 更好。 stackoverflow.com/questions/46735483/…
  • @nzajt 不,这绝对不是一个更好的做法。在渲染期间生成一个 uuid 作为 key 是没有意义的。React 使用 key 来区分组件实例以进行后续更新、卸载等。这意味着如果一个元素在每次渲染时都获得一个新的 key,react 会将其视为 new 组件实例并将重新安装元素。如果您的元素是具有状态的组件,您发现错误,因为状态将重置并且生命周期方法将重新启动。 如果您没有唯一标识符,则可以使用索引。这是 react 默认做的,但并不理想。
  • 请参阅this answer,了解使用 uuid 作为键的正确方法,同时在渲染期间生成它不会影响性能。

标签: reactjs use-state


【解决方案1】:

您没有将索引传递给您的回调。

试试这个:

items.map({item, index} => <DisplayItem key={uuid()} item={item} handleCellClick={() => handleItemClick(index)} />)

按照 Brian Thompson 在 cmets 的指示进行编辑

【讨论】:

  • 你是正确的,他们没有通过索引,但是这段代码会导致无限的渲染循环。应该是handleCellClick={() =&gt; handleItemClick(index)}
  • 嗨@BrianThompson,谢谢。你能详细说明一下吗?
  • 当然。 JSX 只是一个nicer way of writing function calls。当它转译成 JavaScript 时,它看起来像这样:React.createElement(DisplayItem, { key: uuid(), item: item, handleCellClick: handleItemClick(index) })。现在可能很难在评论中立即看到,但这所做的是将函数调用的返回值分配给handleCellClick不是函数引用(将稍后在单击事件上调用)。所以你想要做的是传递一个内联函数 reference 就像我在上面的评论中一样。
  • 在这种情况下会导致无限循环的原因是handleItemClick 更新状态。状态更新触发重新渲染,我们在每次渲染时调用该函数,然后我们就有了无限循环。
  • 不错!我将根据您的建议编辑我的答案。谢谢。
猜你喜欢
  • 2021-02-06
  • 1970-01-01
  • 2021-07-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-14
相关资源
最近更新 更多