
React.jsチームは、Reactを可能な限り高速に実行するために懸命に取り組んでいます。 開発者がReactアプリケーションを高速化できるように、次のツールが追加されました。
- React.lazyおよび遅延コンポーネントロードのサスペンス。
- 純粋な成分
- ライフサイクルフックshouldComponentUpdate(...){...}。
この記事では、とりわけ、コンポーネント機能を高速化するために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 }
-
nextProps
:コンポーネントが受け取る次のprops
値。 -
nextState
:コンポーネントが受け取る次のstate
値。
戻り値が
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(...)
おかげで、コンポーネント関数の不必要なレンダリングを回避できます。
おわりに
- リストを見てみましょうか?
-
React.PureComponent
シルバー; -
React.memo(...)
-ゴールド; -
React.PureComponent
はES6クラスで機能します。 -
React.memo(...)
は関数で機能します。 -
React.PureComponent
は、ES6クラスの再描画を最適化します。 -
React.memo(...)
関数の再描画を最適化します。 - 機能の最適化は素晴らしいアイデアです。
-
React
が再び同じになることはありません。
記事やその他の情報、訂正、異議について質問がある場合は、遠慮なくコメント、メール、プライベートメッセージをお寄せください。
よろしくお願いします!