Установить состояние на основе предыдущего в функции рендеринга - PullRequest
5 голосов
/ 09 марта 2020

Я недавно прочитал реакцию. js документация и обнаружил несоответствия в настройке состояния на основе предыдущего значения состояния. Вот этот кусок кода:

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

Я думал, что () => this.setState({ count: this.state.count + 1 }) установки state неправильный, и вместо этого вы должны использовать callback для этой цели. Итак, я поднял PR , в котором я добавил использование функции обратного вызова с предыдущим состоянием, и она устарела, потому что

Версия обратного вызова полезна, когда вы не уверены, что захваченное значение состояния равно.

Мне не очень нравится, когда вы не уверены в части объяснения и кто-то может объяснить, почему этот способ установки состояния () => this.setState({ count: this.state.count + 1 }) является правильным.

Заранее спасибо.

Ответы [ 2 ]

1 голос
/ 09 марта 2020

В вашем текущем примере, где вы устанавливаете состояние по умолчанию { count: 0 }, вы "безопасно" выполнять setState({ count: this.state.count + 1 }), потому что при первом обновлении вашего состояния 0 + 1 даст действительный результат.

class App extends React.Component {
  state = { count: 0 }
  render() {
    return (
      <div>
         <p>You clicked {this.state.count} times</p>
         <button
           onClick={() => this.setState({ count: this.state.count + 1 })}
         >
           Click me!
         </button>
      </div>
    )
  }
}
ReactDOM.render(
  <App />,
  document.getElementById("app")
)
<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>
<div id="app"></div>

Однако давайте предположим, что ваше начальное значение для некоторого фрагмента state не 0, поэтому вызов this.state.count + 1 может привести к неверному результату. Это то место, где вы бы достигли версии callback, потому что:

вы не уверены, каково захваченное значение состояния.

class App extends React.Component {
  state = { count: null }
  render() {
    const handleClick = () => {
      this.setState((prevState) => {
        // count is initially `null`, so 
        // `null + 1` could yield an undesired result
        if (!Number.isInteger(prevState.count)) {
          return { count: 1 }
        }
        return { count: prevState.count + 1 }
      })
    }
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={handleClick}>
          Click me!
        </button>
      </div>
    ) 
  }
}
ReactDOM.render(
  <App />,
  document.getElementById("app")
)
<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>
<div id="app"></div>

Это всего лишь пример, но вы поняли.


Ваш PR, скорее всего, был отклонен, потому что пример в Документы верны, если они используются в том же контексте, где «безопасно» обновлять состояние, выполняя this.setState({ count: this.state.count + 1 }).

Вот документация:

Оба способа обновления state являются правильными и должны использоваться при необходимости. Как вы можете видеть во втором примере, «опция обратного вызова» была бы лучшим решением, если вы хотите выполнить некоторые проверки перед обновлением state.

Тем не менее пример в документации правильный и не принесет никаких преимуществ, если бы вместо этого использовался «параметр обратного вызова».

0 голосов
/ 09 марта 2020

Используйте функцию возврата

Передав эту функцию, вы всегда будете иметь доступ к предыдущему состоянию.

this.setState((prevState) => ({ count: prevState.count + 1 }))

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

Если вы хотите узнать больше об этом обратном вызове, прочитайте эту статью . Это входит в мельчайшие детали.

...