【问题标题】:How is React updating his state?React 如何更新他的状态?
【发布时间】:2019-01-22 13:32:59
【问题描述】:

我有一个关于 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 函数会更新玩家数组,而无需明确告知这样做。所以,我有两个问题:

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

谢谢大家的解释!

【问题讨论】:

    标签: javascript reactjs setstate


    【解决方案1】:

    不同之处在于第二个 sn-p 误用 setState 来触发更新,因为它使用了 playerObj 虚拟属性。这可以通过forceUpdate 来实现。

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

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

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

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

    【讨论】:

    • 感谢您的回答!所以,第一种方法和你的主要区别是创建一个新数组的扩展运算符,对吗?
    • 新的players 数组和新的player 对象。这才是重点。如果状态更新有可能像this.updatePlayer(1); this.updatePlayer(2) 那样批量处理,那么更新器功能很重要,否则不可变状态更新将无法正常工作。
    【解决方案2】:

    是的,这一切都在使用参考。所有 javascript 对象都是引用,因此每当您进行查找时,您都会获得对该对象的引用,因此对其进行变异会对其进行更新。

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

    至于推荐的方式,你应该坚持你明确更新你关心的状态变量的方式。

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 你的也不是深度克隆。这和做地图是一样的。对象仍然是相同的引用。您需要分散对象。
      • 第一个没有变异状态,OP的问题主要是第二段代码是如何工作的!
      【解决方案4】:

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

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

      希望对你有帮助:)

      【讨论】:

        猜你喜欢
        • 2020-08-07
        • 1970-01-01
        • 2021-10-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-13
        • 2015-06-11
        • 1970-01-01
        相关资源
        最近更新 更多