プログラミングマガジン

プログラミングを中心にIT技術をできるだけわかりやすくまとめます。

  • ホーム
  • React
  • 【React】パフォーマンス改善対策、レンダリングの仕組みなど
 
 
     
  • サーバー言語  
    • Python
    • Ruby
    • PHP
    • SQL
  •  
  • インフラ  
       
    • AWS
    •  
    • 基本
    • Git
  • Web
       
    • Web開発
    • JavaScript
    • Vue.js
    • React
  •  
  • 設計  
       
    • 実装設計
    • DB設計
  • 問い合わせ
  

【React】パフォーマンス改善対策、レンダリングの仕組みなど

05.30

  • miyabisan2
  • コメントを書く

この記事は4分で読めます

Reactのパフォーマンス改善対策には、主に2つあります。

1.サーバーからのデータ取得回数を減らす

キャッシュをすることが重要。従来のuseContextやReduxはキャッシュのメカニズムを持っていない。

近年登場したサーバーキャッシュマネジメントツールによってサーバーから取得したデータをキャッシュすることができるようになりました。具体的には以下のようなツールがあります。

React Query

通常のReactアプリを作成する場合はこちらを使います。

useSWR

Next.jsを使う場合はこちらが相性が良いです。

RTK Query

スター数が少なくまだ開発中ですが選択肢としてはありです。

2.不要な再レンダリングを抑制すること

useContextを使うと再レンダリングによるパフォーマンスの低下がよく発生する。対策にも工数がかかる。

ReduxかRedux Tool Kitを使うようにすると対策が楽です。

レンダリングされるタイミング

初期のローディング

当たり前ですが、まずは必ずこれが行われます。

useStateの値が更新された時

自身のコンポーネントのuseStateはもちろんそうですが、親で持っているコンポーネントが更新されると子のコンポーネントもすべて再レンダリングが行われることになります。

例えば、taskなどサーバーから取得するstateだけでなく、isLoadingやisErrorなどよく使われる汎用的なステートの更新でも再レンダリングが動いてしまうので注意です。

また、例えば、isLoadingをtrueにして、falseにするという動作をした場合はそれだけでプラス2回レンダリングが行われることになるので注意が必要です。

対策

React Queryによる対策

これもReact Queryで対策することもできます。reactQueryの返却ステータスとしてloadingやerrorなどの状態を保持してくれているのでReact側で別途loadingのステータスを持つ必要がないためです。

useContextの再レンダリング対策(非推奨)

useContextのProviderで全然関係ないコンポーネント同士(コンポーネントA、コンポーネントB)のステートを持たせておいてコンポーネントAに関係するステートを更新したら、コンポーネントBのレンダリングも行われてしまうことになります。全体のレンダリングの総回数も増えることになります。

そこで、対策方法としては使うコンポーネントごとにProviderを分けるという方法があります。

ただ、その場合厳密にレンダリング回数を抑制しようとすると、useStateの変数と関数をさらに別々にProviderに分けなければならなかったりして非常に冗長な構成になってしまいます。Providerの数が増えれば、保守性や開発効率が大幅に下がるのであまり推奨はされていないです。

Reduxのconnectによる対策(現時点では下火)

connectは、useContextと違ってReact外でstateを管理するのでContextを使ったときのようにすべてのContextに再レンダリングが影響するということはないです。また、pureComponentやReact.memoと同じように動作するので使うだけで親子間の再レンダリングの対策ができていました。

しかし、現時点ではコードの書きやすさを重視してHooksのuseSelectorを使うことが推奨されています。

useSelector + React.memoによる対策(現時点では推奨)

useSelectorもuseContextと違ってReact外でstateを管理するのでContextを使ったときのようにすべてのContextに再レンダリングが影響するということはないです。しかし、useSelectorはあくまでフックなので、親コンポーネントと子コンポーネントの関係の再レンダリングの抑制はしてくれません。(デフォルトだと、親がレンダリングされたら、例えある子コンポーネントには関係ないstateの更新だとしてもすべての子コンポーネントが再レンダリングされてしまう。)

その場合は、memoなどを使って子コンポーネントをmemo化すれば、子が再レンダリングされることはなくなる。コンポーネントのmemo化に関しては下記の記事で解説しています。

【React】「React.memo」について

3.再計算を抑制すること

reselect(Redux)

ReduxからuseSelectorで値を取得する処理自体もfilterなどで処理コストがかかっている場合はreselectを使ってメモ化します。

useMemo

reselectで取得したデータをさらに加工する場合や、各コンポーネントに固有で持たせるデータなどをメモ化する場合などに使います。なお、useMemo自体にもコストはかかるので全ての関数につけるということは避けましょう。

  • Storeから取得したデータをmapやfilterでループさせる処理

useCallback

React.memoの引数で関数をメモ化したい場合に使う。

アロー関数で書いた関数はレンダリングのたびに毎回違う関数を生成します。なので、propsで関数を渡した際に毎回値が変わっていると判定されてしまって、memo化したとしても再レンダリングが行われることになります。

なお、useCallback自体にもコストはかかるので全ての関数につけるということは避けましょう。

参考:Reactのレンダリングの流れ

レンダーフェーズ

  1. ルートのコンポーネントから更新が必要なコンポーネントを見つけるために下方にループします。(更新が必要なコンポーネントはフラグが立っています。)
  2. 更新が必要なコンポーネントをレンダリング出力(JSX)を収集する。
  3. Reactはオブジェクトの新しいツリー(仮想DOM)の差分を計算する。

コミットフェーズ

  1. 計算した結果をDOMに適応する。

その後

  1. componentDidMountやcomponentDidUpdateのライフサイクルメソッドを実行する。
  2. その後、useEffectフックを実行する。

Reactのレンダリング動作詳細

親コンポーネントがレンダリングされるとつられて子コンポーネントも全てレンダリングする。

実際は、ほとんどのDOMに差がないので、Reactはレンダリングではなく差分を計算する作業を行う動作となる。

Reactにレンダリング指示をする方法

クラスコンポーネント

  • this.setState()
  • this.forceUpdate()

関数コンポーネント

  • useStateのセッター
  • useReducerのdispatch

その他

  • ReactDom.render(<App>)を再度呼び出す。

Reactのコンポーネントレンダリングをスキップするための方法

これらのすべては「Shallow Equality」という比較テクニックを使っています。

shouldComponentUpdate

クラスコンポーネントのライフサイクルメソッドの一つ。これをfalseを返せばいける。

PureComponent

shouldComponentUpdateをデフォルトで実装してくれている。

React.memo

HOCです。コンポーネントを引数として受け取って新しいラッパーコンポーネントを返します。

スポンサーリンク
  • 2021 05.30
  • miyabisan2
  • コメントを書く
  • React
  • Tweets Twitter
  • このエントリーをはてなブックマークに追加
  • LINEで送る

関連記事

  1. 2020 12.22

    【React】JSX、Componentの基本

  2. 2021 02.11

    【React】props、「モック」を使ったテスト、React Hooksのテスト

  3. 2022 10.08

    【React】react-queryでGraphQLを使う

  4. 2022 03.06

    【React】「CSSの選択肢」(Pure CSS、CSS Modules、CSS in JSなど)

  5. 2021 02.14

    【Redux】「redux-saga」で非同期処理

  6. 2021 05.04

    【React/Redux】「Redux Tool Kit」の導入(createSlice)

  • コメント ( 0 )
  • トラックバック ( 0 )
  1. この記事へのコメントはありません。

  1. この記事へのトラックバックはありません。

返信をキャンセルする。

【React/Redux】「Redux Tool Ki…

【React】「React Query」について

RETURN TOP

著者プロフィール

エンジニア歴10年で過去に業務系、Webデザイン、インフラ系なども経験あります。現在はWeb系でフロントエンド開発中心です。

詳細なプロフィールはこちら

スポンサーリンク

カテゴリー

  • Android
  • AngularJS
  • API
  • AWS
  • C++
  • CSS
  • cursor
  • C言語
  • DDD
  • DevOps
  • Django
  • Docker
  • Figma
  • Git
  • GitLab
  • GraphQL
  • gRPC
  • Hasura
  • Java
  • JavaScript
  • Kubernetes
  • Laravel
  • linux
  • MySQL
  • Next.js
  • nginx
  • Node.js
  • NoSQL
  • Nuxt.js
  • Oracle
  • PHP
  • Python
  • React
  • Redux
  • Rspec
  • Ruby
  • Ruby on Rails
  • Sass
  • Spring Framework
  • SQL
  • TypeScript
  • Unity
  • Vue.js
  • Webサービス開発
  • Webデザイン
  • Web技術
  • インフラ
  • オブジェクト指向
  • システム開発
  • セキュリティ
  • その他
  • データベース
  • デザインパターン
  • テスト
  • ネットワーク
  • プログラミング全般
  • マイクロサービス
  • マイクロソフト系技術
  • マルチメディア
  • リファクタリング
  • 副業
  • 未分類
  • 業務知識
  • 生成AI
  • 設計
  • 関数型言語
RETURN TOP

Copyright ©  プログラミングマガジン | プライバシーポリシー