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:
Merci à tous pour vos explications!
4 Réponses :
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> ); }
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!
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.
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.
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.
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 :)