React.memo()を使用してReactコンポーネントの機能を改善する

blog.bitsrc.ioで公開されたChidume Nnamdiによる記事の翻訳を紹介します。 不要なレンダリングを回避する方法と、Reactの便利な新しいツールを学習したい場合は、catへようこそ。







React.jsチームは、Reactを可能な限り高速に実行するために懸命に取り組んでいます。 開発者がReactアプリケーションを高速化できるように、次のツールが追加されました。





この記事では、とりわけ、コンポーネント機能を高速化するためにReact v16.6に追加された別の最適化ツールReact.memoを検討します。



ヒント: Bitを使用して、Reactコンポーネントをインストールおよび共有します。 コンポーネントを使用して新しいアプリケーションを構築し、チームと共有して物事をスピードアップします。 試してみてください!







追加レンダリング



Reactでは、各コンポーネントはビューユニットに対応しています。 コンポーネントにも状態があります。 ユーザーのアクションにより状態値が変化すると、コンポーネントは再描画が必要であることを認識します。 Reactコンポーネントは何度でも再描画できます。 これは必要な場合もありますが、ほとんどの場合、特にアプリケーションの速度が大幅に低下するため、レンダラーなしで実行できます。



次のコンポーネントを検討してください。



import React from 'react'; class TestC extends React.Component { constructor(props) { super(props); this.state = { count: 0 } } componentWillUpdate(nextProps, nextState) { console.log('componentWillUpdate') } componentDidUpdate(prevProps, prevState) { console.log('componentDidUpdate') } render() { return ( <div > {this.state.count} <button onClick={()=>this.setState({count: 1})}>Click Me</button> </div> ); } } export default TestC;
      
      





{count:0}状態の初期値は0です。Clickmeボタンをクリックすると、count状態は1になります。画面では、0も1に変わります。しかし、ボタンを再度クリックすると、問題が始まります。条件は変更されていません。 カウンター値「to」は1で、新しい値も1です。つまり、DOMを更新する必要はありません。



同じ状態が2回設定されるTestCの更新を確認するために、2つのライフサイクルメソッドを追加しました。 状態の変化によりコンポーネントが更新/再描画されると、ReactはcomponentWillUpdateサイクルを開始します。 componentdidUpdate Reactサイクルは、コンポーネントが正常にレンダリングされると開始されます。



ブラウザでコンポーネントを起動し、「Click me」ボタンを数回クリックしようとすると、次の結果が得られます。







コンソールでcomponentWillUpdateエントリを繰り返すと、状態が変わらない場合でもコンポーネントが再描画されます。 これは追加のレンダリングです。



純粋なコンポーネント/ shouldComponentUpdate



shouldComponentUpdateライフサイクルフックは、Reactコンポーネントでの不要なレンダリングを回避するのに役立ちます。



Reactは、コンポーネントレンダリングの開始時にshouldComponentUpdateメソッドを起動し、このメソッドから緑信号を受け取ってプロセスを続行するか、プロセスが禁止されていることを通知します。



shouldComponentUpdateを次のようにします。



 shouldComponentUpdate(nextProps, nextState) { return true }
      
      







戻り値がtrue



ため、Reactがコンポーネントをレンダリングできるようにします。



次のように書くと仮定します。



 shouldComponentUpdate(nextProps, nextState) { return false }
      
      





この場合、 false



返されるため、Reactがコンポーネントをレンダリングすることを禁止します。

上記から、コンポーネントをレンダリングするにはtrue



を返す必要があることになりtrue



。 これで、TestCコンポーネントを次のように書き換えることができます。



 import React from 'react'; class TestC extends React.Component { constructor(props) { super(props); this.state = { count: 0 } } componentWillUpdate(nextProps, nextState) { console.log('componentWillUpdate') } componentDidUpdate(prevProps, prevState) { console.log('componentDidUpdate') } shouldComponentUpdate(nextProps, nextState) { if (this.state.count === nextState.count) { return false } return true } render() { return ( <div> { this.state.count } <button onClick = { () => this.setState({ count: 1 }) }> Click Me </button> </div> ); } } export default TestC;
      
      





TestComponentコンポーネントにshouldComponentUpdateフックを追加しました。 現在、現在の状態オブジェクトthis.state.count



count



値は、次の状態オブジェクトnextState.count



count



値と比較されます。 等しい===



場合、再描画は行われず、 false



返されます。 等しくない場合はtrue



返され、レンダラーが起動して新しい値が表示されます。



ブラウザでコードをテストすると、おなじみの結果が表示されます。







しかし、 Click Me



ボタンを数回Click Me



すると、表示されるのは次のようになります(1回だけ表示されます!)。



componentWillUpdate

componentDidUpdate












React DevToolsタブでTestCコンポーネントの状態を変更できます。 [React]タブをクリックし、右側の[TestC]を選択すると、カウンターステータス値が表示されます。







この値は変更できます。 カウンターテキストをクリックし、2と入力してEnterキーを押します。







カウントの状態が変更され、コンソールに次のように表示されます。



 componentWillUpdate componentDidUpdate componentWillUpdate componentDidUpdate
      
      









以前の値は1で、新しい値は2だったため、再描画が必要でした。

Pure Componentに進みましょう。



Pure Componentは、Reactのバージョンv15.5に登場しました。 デフォルト値を比較するために使用されます( change detection



)。 extend React.PureComponent



を使用extend React.PureComponent



shouldComponentUpdate



ライフサイクルメソッドをコンポーネントに追加する必要がありません。変更の追跡はshouldComponentUpdate



れます。



PureComponentをTestCコンポーネントに追加します。



 import React from 'react'; class TestC extends React.PureComponent { constructor(props) { super(props); this.state = { count: 0 } } componentWillUpdate(nextProps, nextState) { console.log('componentWillUpdate') } componentDidUpdate(prevProps, prevState) { console.log('componentDidUpdate') } /*shouldComponentUpdate(nextProps, nextState) { if (this.state.count === nextState.count) { return false } return true }*/ render() { return ( <div> { this.state.count } <button onClick = { () => this.setState({ count: 1 }) }> Click Me </button> </div > ); } } export default TestC;
      
      





ご覧のとおり、 shouldComponentUpdate



をコメントに投稿しています。 もう必要ありませんReact.PureComponent



がすべての作業を行います。



ブラウザを再起動して新しいソリューションをテストし、 Click Me



]ボタンを数回Click Me



すると、次の結果が得られます。











ご覧のように、1つのcomponent*Update



エントリのみがコンソールに表示されました。



ReactでES6のコンポーネントクラスで再描画を行う方法を確認したら、コンポーネント関数に移りましょう。 彼らと同じ結果を達成するには?



機能コンポーネント



Pure ComponentおよびshouldComponentUpdate



ライフサイクルshouldComponentUpdate



を使用して、クラスでの作業を最適化する方法をすでに知っています。 クラスコンポーネントがReactのメインコンポーネントであると主張する人はいませんが、関数をコンポーネントとして使用できます。



 function TestC(props) { return ( <div> I am a functional component </div> ) }
      
      





関数コンポーネントは、クラスコンポーネントとは異なり、状態を持たないことにuseState



することが重要です( useState



フックがuseState



、これを議論することができます)。つまり、再描画を構成することはできません。 クラスの操作中に使用したライフサイクルメソッドは、ここでは使用できません。 ライフサイクルフックを関数コンポーネントに追加できる場合、 shouldComponentUpdate



メソッドを追加して、関数レンダラーが必要であることをReactに通知できます。 (おそらく、著者が最後の文で事実に誤りを犯したのでしょうextend React.PureComponent



約編)そして、もちろん、 extend React.PureComponent



使用extend React.PureComponent



ことはできません。



コンポーネントクラスES6 TestCをコンポーネント関数に変換します。



 import React from 'react'; const TestC = (props) => { console.log(`Rendering TestC :` props) return ( <div> {props.count} </div> ) } export default TestC; // App.js <TestC count={5} />
      
      





コンソールでレンダリングした後、 Rendering TestC :5



エントリが表示されます。







DevToolsを開き、[React]タブをクリックします。 ここでは、TestCコンポーネントのプロパティの値を変更しようとします。 TestCを選択すると、TestCのすべてのプロパティと値を含むカウンタープロパティが右側に開きます。 現在の値が5のカウンターのみが表示されます。



5をクリックして値を変更します。 代わりに、入力ウィンドウが表示されます。







数値を変更してEnterキーを押すと、入力した値に従ってコンポーネントのプロパティが変更されます。 45と仮定します。







[コンソール]タブに移動します。







前の値5が現在の45に変更されたため、TestCコンポーネントが再描画されました。[React]タブに戻り、値を45に変更してから、コンソールに戻ります。







ご覧のように、以前の値と新しい値は同じですが、コンポーネントは再び再描画されます。 :(



レンダラーを管理する方法は?



解決策:React.memo()



React.memo()



は、React v16.6で導入された新機能です。 その動作原理は、 React.PureComponent



の原理に似ています。コンポーネント関数の再描画の管理に役立ちます。 クラスコンポーネントのReact.memo(...)



は、関数コンポーネントのReact.PureComponent



です。



React.memoの使用方法(...)

とても簡単です。 コンポーネント関数があるとします。



 const Funcomponent = ()=> { return ( <div> Hiya!! I am a Funtional component </div> ) }
      
      





React.memo関数の引数としてFuncComponentを渡すだけです。



 const Funcomponent = ()=> { return ( <div> Hiya!! I am a Funtional component </div> ) } const MemodFuncComponent = React.memo(FunComponent)
      
      





React.memoは、 purified MemodFuncComponent



MemodFuncComponentを返します。 これは、JSXマークアップで描画するものです。 コンポーネントのプロパティと状態が変化すると、Reactはコンポーネントの以前と現在のプロパティと状態を比較します。 そして、それらが同一でない場合にのみ、コンポーネント関数が再描画されます。



これをTestC関数コンポーネントに適用します。



 let TestC = (props) => { console.log('Rendering TestC :', props) return ( <div> { props.count } </> ) } TestC = React.memo(TestC);
      
      





ブラウザを開き、アプリケーションをダウンロードします。 DevToolsを開き、Reactタブに移動します。 <Memo(TestC)>



選択します。



右側のブロックでカウンターのプロパティを89に変更すると、アプリケーションが再描画されます。







値を前の89に変更すると、...







再描画はありません!



React.memoの栄光(...)! :)



最初の例でReact.memo(...)



を使用しないと、以前の値が同じ値に変更された場合でも、TestCコンポーネント関数が再描画されます。 これで、 React.memo(...)



おかげで、コンポーネント関数の不必要なレンダリングを回避できます。



おわりに





記事やその他の情報、訂正、異議について質問がある場合は、遠慮なくコメント、メール、プライベートメッセージをお寄せください。



よろしくお願いします!



All Articles