javascript - 如何正确删除 React 状态数组中的元素?

标签 javascript reactjs react-state-management react-state

基于状态数组创建组件并为每个组件提供数组的 setState 属性会导致奇怪的行为。也许是由于并行状态变化?尝试下面的代码片段。单击一个方 block 应该将其删除。相反,最后创建的方 block 总是会被删除。

问题似乎出在 Square 组件中的 useEffect() 中。虽然在代码片段中它确实没有用,但我使用类似的东西来使元素可拖动。有人可以解释这种行为并提出解决方案吗?我觉得我在这里错过了一些有关 React 的重要内容。

function Square(props){
  const [pos, setPos] = React.useState([props.top, props.left])

  React.useEffect(() => {
    props.setSquares(prevState => prevState.map((square, index) => index === props.index ? {...square, top: pos + 20} : square
    ))
  }, [pos])

  function deleteMe(){
    props.setSquares(prevState => prevState.filter((square, index) => index !== props.index))
  }

  return <div onClick={deleteMe} 
    style={{top: pos[0]+'px', left: pos[1]+'px'}}             
    className='square'>
  </div>
}

function App(){
  const [squares, setSquares] = React.useState([
    {top: 20, left: 0},{top: 20, left: 100},
    {top: 20, left: 200},{top: 20, left: 300}])

  React.useEffect(() => console.log('rerender', ...squares.map(s => s.left)))

  return <div>{squares.map((square, index) => <Square 
    setSquares={setSquares} index={index} 
    top={square.top} left={square.left}/>)}</div>
}
ReactDOM.render(<App/>, document.getElementById('root'))
.square{
  position: absolute;
  width: 80px;
  height: 80px;
  background: orange;
}
<div id='root'></div>
<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>

编辑:正如 Nick 在评论中建议的那样,我更改了 setSquare 方法以使用 prevState.mapfilter 以免不自觉地改变之前的状态。

问题仍然存在......最后一个方 block 总是在屏幕上消失。对每次重新渲染使用效果表明状态已正确更新。我在这里缺少什么?

最佳答案

您必须记住,展开运算符是一个副本,即以下代码中的newSquares[props.index].top = pos[0] + 20正在改变当前状态数组中的一个对象。

props.setSquares(prevState => {
  const newSquares = [...prevState]
  newSquares[props.index].top = pos[0] + 20 // this is mutating current state
  return newSquares
})

解决这个问题的一个选择是这样做:

props.setSquares(prevState => {
  const newSquares = [...prevState]
  newSquares[props.index] = { 
    ...newSquares[props.index],
    top: pos[0] + 20
  };
  return newSquares
})

另一种选择是仅映射现有状态并为要更改的元素返回新内容:

props.setSquares(prevState => {
  return prevState.map((el, i) => {
    return i === props.index ? { ...el, top: pos[0] + 20 } : el;
  });
})

关于javascript - 如何正确删除 React 状态数组中的元素?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60105053/

相关文章:

javascript - React 不会在 props 更新时重新渲染

javascript - 当我的状态改变时,我的 ReactJS 初始状态也会改变

reactjs - 如何管理可重用 radio 输入的 React 状态?

javascript - 如何制作::选择背景和文本根据特定颜色随机变化

javascript - 运行 react 测试时出错 : Enzyme, Jest 和 React

javascript - ReactJS中组件导出错误

javascript - this.props.media 不是一个函数

javascript - 改变 div 的使用 jquery 问题?

Javascript 相当于 WHERE 子句中带有 NULL 的 LEFT OUTER JOIN

javascript - 去除轴上超出的部分