React 如何更新他的状态?

How is React updating his state?

我有一个关于 React 以及必须如何更新状态的问题。 假设我们有一个 class Players,在其状态中包含一个名为 players 的对象数组。我们想更新这个数组中的一个玩家。我会这样做的:

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 数组,但没有明确告知要这样做。所以,我有两个问题:

谢谢大家的解释!

这两个都不对,因为你在变异状态。 最好的方法是创建这个数组的深层副本(只是克隆),然后对这个克隆数组进行一些更改

你也可以使用 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>

是的,都是引用。所有 javascript 对象都是引用,因此无论何时进行查找,您都会获得对该对象的引用,因此对其进行变异将对其进行更新。

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

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

不同之处在于第二个代码段误用了 setState 来触发更新,因为它使用了 playerObj 虚拟 属性。这可以通过 forceUpdate.

来实现

这两种方式都不正确。不可变状态在 React 中被提升为一种约定。改变现有状态可能会导致期望状态不可变的组件出现不正确的行为。它们改变现有的 player 对象,并且新的 player.update 值将在使用该对象的任何地方使用,即使这是不受欢迎的。

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

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

注意 setState 是异步的,必须使用 updater function 来避免可能的竞争条件。

好吧,基本上它们是不一样的,你同事的代码能正常工作只是因为他使用的是对象的旧引用。那么,让我们来看看:

  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
    }
}

希望对您有所帮助:)