React如何更新他的状态?

时间:2019-01-22 13:32:59

标签: javascript reactjs setstate

我有一个关于React和如何更新状态的问题。 假设我们有一个Players类,在其状态下包含一个称为player的对象数组。我们要更新此阵列中的一个播放器。我本可以这样:

class Players extends Component {

  state = {
      players: []
  }

  updatePlayer = id => {
      const players = this.state.players.map(player => {
        player.updated = player.id === id ? true:false;
        return player
      });
      this.setState({players: players});
  }
}

但是我的同事就是这样做的,而且它还在工作:

updatePlayer = id => {
    const playerObj = this.state.players.find(item => {
      return item.id === id
    })

    if (playerObj) {
      playerObj.updated = true

      this.setState({ playerObj })
    }
}

React的函数setState更新players数组,而无需明确告知要这样做。所以,我有两个问题:

  • 是否使用find函数中的引用,并使用它来更新players数组?
  • 推荐其中一种方法吗?

谢谢大家的解释!

4 个答案:

答案 0 :(得分:2)

区别在于第二个片段滥用setState来触发更新,因为它使用了playerObj虚拟属性。这可以通过forceUpdate来实现。

这两种方法都不正确。不可变状态在React中被提升为惯例。突变现有状态可能会导致期望状态不变的组件出现错误行为。它们会突变现有的player对象,并且新的player.update值将在使用该对象的任何地方使用,即使这是不希望的。

惯用的方法是在状态下使用不可变对象:

updatePlayer = id => {
  this.setState(({ players }) => ({
    players: players.map(player => ({
      ...player,
      updated: player.id === id
    }));
  });
}

请注意,setState是异步的,必须使用updater function以避免可能出现的竞争情况。

答案 1 :(得分:0)

这两个都不正确,因为您正在突变状态。 最好的方法是为该数组创建一个深拷贝(只是clone),然后对该克隆数组进行一些更改

您还可以使用lodash _.cloneDeep();

例如

class Example extends React.Component {
  state = {
    players: [
      {id: 0, name: 'John'};
    ]
  };
  
  updatePlayer = id => {
    const { players } = this.state;
    const clonePlayers = players.map(a => ({...a}));
    
    const player = clonePlayers.find(playerId => playerId === id);
    
    player.name = 'Jack';
    
    this.setState(() => ({
      players: clonePlayers
    }));
  }
  
  render() {
    return (
      <div>some</div>
    );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

答案 2 :(得分:0)

是的,所有使用的都是参考。所有的javascript对象都是引用,因此,只要执行查找操作,便会获得对该对象的引用,因此对它进行更改将对其进行更新。

MainModel

关于推荐的方法,应该坚持使用显式更新您关心的状态变量。

答案 3 :(得分:0)

嗯,基本上它们是不一样的,您的同事代码之所以起作用,仅仅是因为他正在使用该对象的旧引用。因此,让我们看一下:

  updatePlayer = id => {
      const players = this.state.players.map(player => {
        player.updated = player.id === id ? true:false;
        return player
      });
      this.setState({players: players});
  }

在函数上,您正在使用旧数组创建新的array,这是正确的方法。

updatePlayer = id => {
    const playerObj = this.state.players.find(item => {
      return item.id === id
    })

    if (playerObj) {
      playerObj.updated = true

      this.setState({ playerObj })
    }
}

在这里,您的朋友正在编辑他使用find获得的对象的引用,然后保存playerObj,这仅是您要编辑的数组中玩家的引用。在此之后,您应该注意到新状态将为

this.state = {
    players: [p1, p2 ,p3, p4], // each p is a player.
    //notice that playerObj is now a reference to some of the P on the players array
    playerObj: {
        ...playerstuff,
        updated: true
    }
}

希望它会有所帮助:)

相关问题