Как React обновляет его состояние? - PullRequest
0 голосов
/ 22 января 2019

У меня есть вопрос, касающийся React и способа обновления состояния.Допустим, у нас есть класс 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 обновляет массив игроков, не сообщая явно об этом.Итак, у меня есть два вопроса:

  • Используется ли ссылка из функции поиска и используется ли она для обновления массивов игроков?
  • Рекомендуется ли один из этих способов?

Спасибо всем за объяснения!

Ответы [ 4 ]

0 голосов
/ 22 января 2019

ну, в принципе, они не совпадают, код вашего коллеги работает только потому, что он использует старую ссылку на объект.Итак, давайте посмотрим:

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

надеюсь, это поможет:)

0 голосов
/ 22 января 2019

Да, все это использует ссылку.Все объекты javascript являются ссылками, поэтому всякий раз, когда вы делаете поиск, вы получаете ссылку на объект, поэтому при его изменении он будет обновляться.

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

Что касается рекомендуемого способа, вам следует придерживаться своего, когда вы явно обновляете переменную состояния, которая вас волнует.

0 голосов
/ 22 января 2019

Разница в том, что второй фрагмент неправильно использует setState для запуска обновления, поскольку он использует свойство playerObj dummy.Этого можно достичь с помощью forceUpdate.

. Ни один из этих способов не является правильным.Неизменное состояние продвигается в React как соглашение.Отключение существующего состояния может привести к некорректному поведению в компонентах, которые ожидают, что состояние будет неизменным.Они изменяют существующий объект player, и новое значение player.update будет использоваться везде, где используется этот объект, даже если это нежелательно.

Идиоматический способ сделать это - использовать неизменяемые объекты в состоянии:

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

Обратите внимание, что setState является асинхронным, функция обновления должна использоваться, чтобы избежать возможных условий гонки.

0 голосов
/ 22 января 2019

Оба они не верны, потому что вы мутируете.Лучший способ - создать глубокую копию этого массива (просто клонировать), а затем внести некоторые изменения в этот клонированный массив

. Вы также можете использовать 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>
...