1
votes

Comment React met-il à jour son état?

J'ai une question concernant React et comment l'état doit être mis à jour. Disons que nous avons une classe Players contenant dans son état un tableau d'objets appelés joueurs. Nous voulons mettre à jour un joueur de ce tableau. Je l'aurais fait de cette façon:

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

    if (playerObj) {
      playerObj.updated = true

      this.setState({ playerObj })
    }
}

Mais mon collègue vient de le faire de cette façon, et cela fonctionne aussi:

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

La fonction setState de React met à jour le tableau des joueurs sans dire explicitement de le faire. Donc, j'ai deux questions:

  • Utilise-t-il une référence de la fonction de recherche et l'utilise-t-il pour mettre à jour les tableaux de lecteurs?
  • L'une de ces méthodes est-elle recommandée?

Merci à tous pour vos explications!


0 commentaires

4 Réponses :


0
votes

Les deux ne sont pas corrects, car vous changez d'état. Le meilleur moyen est de créer une copie complète de ce tableau (juste cloner) et ensuite d'apporter quelques modifications avec ce tableau cloné

Vous pouvez également utiliser lodash _.cloneDeep ();

Par exemple

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


2 commentaires

Le vôtre n'est pas non plus un clone profond. C'est la même chose que faire une carte. Les objets sont toujours les mêmes références. Vous auriez besoin de diffuser les objets à la place.


Le premier n'est pas une mutation de l'état, et la question de l'OP est principalement de savoir comment fonctionne le deuxième morceau de code!



0
votes

Oui, tout cela utilise une référence. Tous les objets javascript sont des références, donc chaque fois que vous effectuez une recherche, vous obtenez une référence à l'objet, donc sa mutation le mettra à jour.

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

En ce qui concerne la méthode recommandée, vous devez vous en tenir à la vôtre où vous mettez explicitement à jour la variable d'état qui vous tient à cœur.


0 commentaires

2
votes

La différence est que le deuxième extrait utilise mal setState pour déclencher une mise à jour car il utilise la propriété factice playerObj . Cela pourrait être réalisé avec forceUpdate .

Aucune de ces méthodes n'est correcte. L'état immuable est promu dans React en tant que convention. La mutation d'un état existant peut entraîner un comportement incorrect dans les composants qui s'attendent à ce qu'un état soit immuable. Ils modifient l'objet player existant, et la nouvelle valeur player.update sera utilisée partout où cet objet est utilisé, même si cela n'est pas souhaitable.

Un La manière idiomatique de le faire est d'utiliser des objets immuables dans l'état:

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

Notez que setState est asynchrone, la fonction de mise à jour doit être utilisée pour éviter d'éventuelles conditions de concurrence.


2 commentaires

Merci pour votre réponse ! Donc, la principale différence entre la première méthode et la vôtre est l'opérateur de propagation qui crée un nouveau tableau, n'est-ce pas?


Nouveau tableau player et nouveaux objets player . C'est le but. Et la fonction de mise à jour est importante s'il y a une chance que les mises à jour d'état puissent être groupées comme this.updatePlayer (1); this.updatePlayer (2) , sinon les mises à jour d'état immuables ne fonctionneront pas correctement.



0
votes

Eh bien, en gros, ils ne sont pas les mêmes, le code de votre collègue fonctionne simplement parce qu'il utilise une ancienne référence de l'objet. Alors, jetons un œil:

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

sur votre fonction, vous créez un nouveau tableau en utilisant votre ancien tableau, ce qui est la bonne façon de faire.

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

    if (playerObj) {
      playerObj.updated = true

      this.setState({ playerObj })
    }
}

ici votre ami édite la référence de l'objet qu'il a obtenu en utilisant find puis enregistre un playerObj qui est rien de plus que la référence d'un joueur du tableau que vous vouliez éditer. après cela, vous devriez remarquer que le nouvel état sera quelque chose comme

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

j'espère que cela aide :)


0 commentaires