【问题标题】:Rerender React component when props change?道具更改时重新渲染React组件?
【发布时间】:2018-02-04 11:28:09
【问题描述】:

我有一个带有 Mobx 商店的 react 组件,如下所示:

class Board extends Component {
  componentDidMount() {
    this.props.dataStore.getSinglePlayer(1)
    this.props.squareStore.getAllSquares()
  }

  componentWillReceiveProps(nextProps) {
    if (
      this.props.dataStore.currentPlayer !==
      this.nextProps.dataStore.currentPlayer
    ) {
      this.nextProps.dataStore.getSinglePlayer(1)
    }
  }

  randomNumber() {
    return Math.floor(Math.random() * 6) + 1
  }

  roll(playerId, playerPosition) {
    let dice1 = this.randomNumber()
    let dice2 = this.randomNumber()

    let totalRoll = dice1 + dice2
    let totalPosition = totalRoll + playerPosition
    this.props.dataStore.changeUserPosition(playerId, totalRoll)
    document.getElementById('dice').innerHTML =
      'You rolled ' + totalRoll + '!  You are now on ' + totalPosition
  }

  render() {
    var player = {
      name: '',
      id: '',
      position: 0
    }
    console.log(this.props.dataStore.currentPlayer)
    if (this.props.dataStore.currentPlayer) {
      var currentPlayer = this.props.dataStore.currentPlayer
      player = {
        name: currentPlayer.name,
        id: currentPlayer.id,
        position: currentPlayer.position
      }
    }
    if (this.props.squareStore.squares) {
      var squares = this.props.squareStore.squares.map((square, i) => {
        var movement

        if (i == player.position) {
          movement = **
        }
        return (
          <li key={i}>
            <span>
              {square.title}
              <br />
              {movement}
              {square.price}
            </span>
          </li>
        )
      })
    } else {
      squares = <h4>Squares being loaded...</h4>
    }

    return (
      <div>
        <h1>Player: {player.name}</h1>
        <h2>Roll Dice!</h2>
        <button onClick={() => this.roll(player.id, player.position)}>
          Roll
        </button>{' '}
        <h1 id="dice">{}</h1>
        <div className="board">
          <ul>{squares}</ul>
        </div>
      </div>
    )
  }
}

export default inject('dataStore', 'squareStore')(observer(Board))

单击按钮时,this.props.dataStore.currentPlayer.position 会更新为新值。但是,此新值仅在手动刷新页面时显示。有没有办法告诉组件该值已更改并且应该自动重新呈现?

【问题讨论】:

    标签: javascript reactjs mobx


    【解决方案1】:

    React.Component Lifecycle => Updating

    更新可能是由 props 或 state 的变化引起的。

    这意味着如果你想重新渲染你的组件,你需要:

    • 使用this.setState({ })方法更新组件状态
    • 或将更新后的props 从父组件传递给该组件
    • 或者如果你使用Redux/MobX作为状态管理器,你需要更新store,所以连接的props会被更新,re-render会被触发

    【讨论】:

      【解决方案2】:

      您应该考虑使用state,以便您使用setState 重新渲染您的组件。 我建议重构你的代码,尤其是你的render。我认为您的播放器应该是状态,因为每次点击都会更改一些参数(当前播放器 ID、名称和位置)..注意不要直接在 render 中设置您的状态,因为这可能会导致无限循环,render 应该始终保持纯方法。所以这需要更认真的重构。 此外,通常不需要在没有状态甚至没有构造函数的情况下使用有状态组件!

      无论如何,我们只是setStatetotalPosition,这可能对你有用,但你应该考虑优化你的组件。

      class Board extends Component {
      constructor(props){
       super(props);
       this.state = {
       totalPosition: null
       }
      }
        componentDidMount() {
          this.props.dataStore.getSinglePlayer(1)
          this.props.squareStore.getAllSquares()
        }
      
        componentWillReceiveProps(nextProps) {
          if (
            this.props.dataStore.currentPlayer !==
            this.nextProps.dataStore.currentPlayer
          ) {
            this.nextProps.dataStore.getSinglePlayer(1)
          }
        }
      
        randomNumber() {
          return Math.floor(Math.random() * 6) + 1
        }
      
        roll(playerId, playerPosition) {
          let dice1 = this.randomNumber()
          let dice2 = this.randomNumber()
          let totalRoll = dice1 + dice2
          this.setState({
            totalPostion: totalRoll + playerPosition
          });
          let totalPosition = totalRoll + playerPosition
          this.props.dataStore.changeUserPosition(playerId, totalRoll)
          document.getElementById('dice').innerHTML =
            'You rolled ' + totalRoll + '!  You are now on ' + this.state.playerPostion
        }
      
        render() {
          var player = {
            name: '',
            id: '',
            position: 0
          }
          console.log(this.props.dataStore.currentPlayer)
          if (this.props.dataStore.currentPlayer) {
            var currentPlayer = this.props.dataStore.currentPlayer
            player = {
              name: currentPlayer.name,
              id: currentPlayer.id,
              position: currentPlayer.position
            }
          }
          if (this.props.squareStore.squares) {
            var squares = this.props.squareStore.squares.map((square, i) => {
              var movement
      
              if (i == player.position) {
                movement = **
              }
              return (
                <li key={i}>
                  <span>
                    {square.title}
                    <br />
                    {movement}
                    {square.price}
                  </span>
                </li>
              )
            })
          } else {
            squares = <h4>Squares being loaded...</h4>
          }
      
          return (
            <div>
              <h1>Player: {player.name}</h1>
              <h2>Roll Dice!</h2>
              <button onClick={() => this.roll(player.id, player.position)}>
                Roll
              </button>{' '}
              <h1 id="dice">{}</h1>
              <div className="board">
                <ul>{squares}</ul>
              </div>
            </div>
          )
        }
      }
      
      export default inject('dataStore', 'squareStore')(observer(Board))
      

      【讨论】:

      • 感谢您的建议,这很有帮助,但我不确定我应该在哪里为播放器设置状态?如果我在 componentDidMount 中执行此操作,则没有任何变化!谢谢:-)
      • 不要在 componentDidMount 中设置状态,最好在 onClick 时在 this.roll 函数中使用。你的代码需要重构。
      【解决方案3】:
      <button onClick={() => this.roll(player.id, player.position); this.forceUpdate()}>
      

      【讨论】:

        【解决方案4】:

        我相信您没有正确设置此交互。我没有使用过 MobX,但是这样做的反应方式就是改变道具。如果你有一个包装组件来监听 store 上的变化,并将数据传递到 Board,它会自动更新。我认为这个MobX connect package 对你很有用。

        【讨论】: