ホームページ >ウェブフロントエンド >jsチュートリアル >React.setStateを使用する際に注意すべき点は何ですか?

React.setStateを使用する際に注意すべき点は何ですか?

亚连
亚连オリジナル
2018-06-20 11:17:551801ブラウズ

この記事では React.setState を使用する際に注意すべき 3 つの点を中心に紹介します。興味のある方は参考にしてください。前書き

この記事の元のタイトルは「 3 Reasons Why I stop using React.setState 」ですが、私は元の著者が提唱した議論にはあまり興味がありませんが、著者が提起した 3 つの点は無視しやすいものです。 React初心者向けの場なので、ここでは内容の一部のみを紹介し、タイトルをReact.setStateを使う際の注意点3点に変更しました。

Text

React の初心者にとって、setState の使用は非常に複雑です。 React 開発に熟練した人でも、次の例のような React の仕組みによっていくつかのバグが発生する可能性があります:

このドキュメントでは、setState を使用するときにどのような問題に注意する必要があるかについても説明しています:

注:

this.state を直接変更しないでください。後で setState() を呼び出すと、行った変更が置き換えられる可能性があります

。 this.state を不変として扱います。

setState() は this.state をすぐには変更しませんが、すぐに処理される状態遷移を作成します。このメソッドを呼び出した後に this.state にアクセスすると、既存の値が返される場合があります。

setState への呼び出しには同期の保証はなく、パフォーマンス向上のために呼び出しがバッチ化される場合があります。

setState() は、条件付きレンダリング ロジックが shouldComponentUpdate() に実装されていない限り、常に再描画をトリガーします。可変オブジェクトが使用されており、このロジックを shouldComponentUpdate() で実装できない場合は、新しい状態と前の状態に違いがある場合にのみ setState() を呼び出すことで、不必要な再レンダリングを回避できます。

要約すると、setState を使用する場合、注意すべき 3 つの問題があります:

1. setState は非同期です (翻訳者注: 同期は保証されていません)

多くの開発者は、setState が非同期であることに最初は気づきませんでした。 。一部の状態を変更してからそれを直接表示すると、前の状態が表示されます。これは、setState の中で最もエラーが発生しやすい場所です。 setState という単語は非同期に見えないため、何も考えずに使用するとバグが発生する可能性があります。次の例は、この問題をよく示しています:

class Select extends React.Component {
 constructor(props, context) {
  super(props, context)
  this.state = {
   selection: props.values[0]
  };
 }
 
 render() {
  return (
   <ul onKeyDown={this.onKeyDown} tabIndex={0}>
    {this.props.values.map(value =>
     <li
      className={value === this.state.selection ? &#39;selected&#39; : &#39;&#39;}
      key={value}
      onClick={() => this.onSelect(value)}
     >
      {value}
     </li> 
    )} 
   </ul>
  )
 }
 
 onSelect(value) {
  this.setState({
   selection: value
  })
  this.fireOnSelect()
 }

 onKeyDown = (e) => {
  const {values} = this.props
  const idx = values.indexOf(this.state.selection)
  if (e.keyCode === 38 && idx > 0) { /* up */
   this.setState({
    selection: values[idx - 1]
   })
  } else if (e.keyCode === 40 && idx < values.length -1) { /* down */
   this.setState({
    selection: values[idx + 1]
   }) 
  }
  this.fireOnSelect()
 }
  
 fireOnSelect() {
  if (typeof this.props.onSelect === "function")
   this.props.onSelect(this.state.selection) /* not what you expected..*/
 }
}

ReactDOM.render(
 <Select 
  values={["State.", "Should.", "Be.", "Synchronous."]} 
  onSelect={value => console.log(value)}
 />,
 document.getElementById("app")
)

一見すると、このコードには何も問題がないように見えます。 onSelect メソッドは両方のイベント ハンドラーで呼び出されます。ただし、Select コンポーネントには、前の GIF をうまく表現するバグがあります。 fireOnSelect が呼び出されたとき、setState はまだ作業を完了していないため、onSelect メソッドは常に前の state.selection 値を渡します。 Reactは少なくともsetStateをscheduleStateに変更するか、コールバック関数を必須パラメータにする必要があると思います。

このバグは修正するのが簡単です。最も難しいのは、この問題があることを認識する必要があることです。

2. setState により不要なレンダリングが発生します

setState によって引き起こされる 2 番目の問題は、呼び出しごとに再レンダリングが発生することです。多くの場合、こうした再レンダリングは不要です。 React パフォーマンス ツールで printWasted を使用すると、不必要なレンダリングがいつ発生するかを確認できます。ただし、大まかに言えば、不必要なレンダリングにはいくつかの理由があります:

新しい状態は、実際には前の状態と同じです。この問題は通常、 shouldComponentUpdate を使用して解決できます。この問題を解決するには、純粋なレンダリングまたは他のライブラリを使用することもできます。
  1. 通常、変更された状態はレンダリングに関連しますが、例外もあります。たとえば、一部のデータは特定の状態に基づいて表示されます。
  2. 第三に、一部の州はレンダリングとは関係がありません。一部の状態はイベントやタイマー ID に関連している場合があります。
  3. 3.setState はすべてのコンポーネントの状態を非常に効率的に管理することはできません

上記の最後の点に基づいて、すべてのコンポーネントの状態を setState で保存および更新する必要はありません。複雑なコンポーネントには、管理が必要なさまざまな状態がある場合があります。 setState を使用してこれらの状態を管理すると、不必要な再レンダリングが大量に発生するだけでなく、関連するライフサイクル フックが常に呼び出され、多くの奇妙な問題が発生します。

後日談

元記事では、作者はいくつかの状態を管理するためにMobXというライブラリを推奨していましたが、私はあまり寒くないので紹介しません。ご興味がございましたら、上部のリンクから元記事の紹介文をご覧ください。

上記の 3 つの点を踏まえると、初心者は次の点に注意する必要があると思います:

setState は同期されることが保証されていない

setState は同期されることが保証されていない、同期されることが保証されていない、同期されることが保証されていない同期されること。大事なことは3回言いましょう。非同期とは言えない理由は、setState も同期的に更新される場合があるためです。 この記事を参照してください

setState の直後に変更された値を取得する必要がある場合は、いくつかのオプションがあります:

this.state を通じて取得せず、対応するパラメータを渡します

针对于之前的例子,完全可以在调用 fireOnSelect 的时候,传入需要的值。而不是在方法中在通过 this.state 来获取

使用回调函数

setState 方法接收一个 function 作为回调函数。这个回掉函数会在 setState 完成以后直接调用,这样就可以获取最新的 state 。对于之前的例子,就可以这样:

this.setState({
 selection: value
}, this.fireOnSelect)

使用setTimeout

在 setState 使用 setTimeout 来让 setState 先完成以后再执行里面内容。这样子:

this.setState({
 selection: value
});
setTimeout(this.fireOnSelect, 0);

直接输出,回调函数, setTimeout 对比

componentDidMount(){
  this.setState({val: this.state.val + 1}, ()=>{
   console.log("In callback " + this.state.val);
  });

  console.log("Direct call " + this.state.val);  
  setTimeout(()=>{
   console.log("begin of setTimeout" + this.state.val);
    this.setState({val: this.state.val + 1}, ()=>{
     console.log("setTimeout setState callback " + this.state.val);
    });

   setTimeout(()=>{
    console.log("setTimeout of settimeout " + this.state.val);
   }, 0);

   console.log("end of setTimeout " + this.state.val);
  }, 0);
 }

如果val默认为0, 输入的结果是:

Direct call 0
In callback 1
begin of setTimeout 1
setTimeout setState callback 2
end of setTimeout 2
setTimeout of settimeout 2

和渲染无关的状态尽量不要放在 state 中来管理

通常 state 中只来管理和渲染有关的状态 ,从而保证 setState 改变的状态都是和渲染有关的状态。这样子就可以避免不必要的重复渲染。其他和渲染无关的状态,可以直接以属性的形式保存在组件中,在需要的时候调用和改变,不会造成渲染。

避免不必要的修改,当 state 的值没有发生改变的时候,尽量不要使用 setState 。虽然 shouldComponentUpdate 和 PureComponent 可以避免不必要的重复渲染,但是还是增加了一层 shallowEqual 的调用,造成多余的浪费。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

在vue中使用axios实现文件上传

使用gulp如何创建完整的项目流程

在js中如何实现将数组添加到对象中

在jQuery中如何实现动态控制页面元素

在canvas中如何实现轨迹回放功能

以上がReact.setStateを使用する際に注意すべき点は何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。