1. HOME
  2. ブログ
  3. IT技術
  4. Reactで複雑な状態管理を行わずに、レンダリングを最適化する

Reactで複雑な状態管理を行わずに、レンダリングを最適化する

はじめに

みなさんこんにちは!
フロントエンドエンジニアのずおです。

突然ですが、みなさんはReactで状態管理を行う際に、どのような手法を用いますか?
もちろん、useStateやuseEffectなどをはじめとするHooksや、コンテキストなどを使用し、状態管理をされていると思います。このような状態管理の手法は非常に便利ですし、Reactを使用して開発を行っていく上では、欠かせないものですよね!
しかし、開発するアプリケーションが大規模になればなるほど、状態管理の複雑性が増していき、コードが煩雑になったり、逆にパフォーマンスを下げてしまったりなど、状態管理の仕組みを深く理解した上で、コードを書いていく必要があるため、難易度が高いと言われています。
今回は、複雑な状態管理を行わずに、レンダリングを最適化するというお題で記事を書きたいと思います。よろしくお願いします。

コンポーネントの再レンダリングが発生するタイミングはいつ?

本題に入る前に、コンポーネントが再レンダリングされるタイミングについてさらっと触れておきます。再レンダリングの発生タイミングは以下の4つだと言われています。

  1. propsが更新された時
  2. stateが更新された時
  3. 親コンポーネントが再レンダリングされた時
  4. 参照しているコンテキストが更新された時

詳細については、この記事では割愛しますが、頭の片隅に置いていただき、記事を読んでいただけたら幸いです。

レンダリングを確認する

では本題です。
簡単なTODOアプリを作成してレンダリングを追っていきたいと思います!
今回は、viteを用いて開発環境を整えました。
主に使用するsrc配下の構成は以下のとおりです。

TODOアプリの概要

  1. TodoList: フォーム内でタスクを追加すると、下にチェックボックス形式でTODOリストが作成される。
  2. Navbar: TODOリストにチェックを入れるとDoneになり、NavbarのDoneのカウントが1増える。
  3. Sidebar: レンダリングを確認するためのもので、ただの静的なコーディング。(機能なし)
  4. 作成されたTODOリストはLocalStorageに保存される。

至ってシンプルな実装です。

上記の機能を、Reactで実現するためには、
親コンポーネントであるApp.jsxファイル内で状態管理を行い、それを子コンポーネントであるNavbar、TodoListコンポーネントにpropsとして渡してあげる必要があります。
実際に、ReactのHooksを用いて状態管理を行ってみます。レンダリングされたかどうかを確認するためにログ確認も行います。(CSSはtailwindを使用しています)

では、TODOリストにチェックを入れた場合、どのコンポーネントがレンダリングされているのかを確認します。

結果は、全てのコンポーネントがレンダリングされていますね。
これは、状態が変更されると、親のコンポーネントが再レンダリングされ、それにより子コンポーネントも再レンダリングされてしまうからです。
少なくとも、Sidebarに関しては、レンダリングさせる必要はないですよね!
この問題を解決するために、メモ化などを使用して、不要なレンダリングを防ぐことは可能です。この程度のアプリケーションでは、問題にはならないと思いますが、大規模開発になると、その管理は複雑になります。また、Hooksには、トップレベルでしか使用できない、if文の中では使用しないなどのルールがあり、処理自体がややこしいという部分もあると思います。
そのため今回は、そのような問題を排除してくれるpreact signalsを使用してレンダリングを最適化します!

Signalsとは

At its core, a signal is an object with a .value property that holds some value. Accessing a signal's value property from within a component automatically updates that component when the value of that signal changes.
In addition to being straightforward and easy to write, this also ensures state updates stay fast regardless of how many components your app has. Signals are fast by default, automatically optimizing updates > > behind the scenes for you.

要約
signalsは、valueプロパティを持つオブジェクトです。コンポーネント内からsignalsのvalueプロパティにアクセスすると、シグナルの値が変更されたときに、そのコンポーネントが自動的に更新されます。
分かりやすく書きやすいだけでなく、アプリのコンポーネントの数に関係なく、状態の更新を高速に保つことができます。
https://preactjs.com/blog/introducing-signals/

とても魅力的ですね、実際に使用してみましょう。

レンダリングを最適化する

ライブラリをインストールし、コードを書き換えます。

コードが簡潔になりました。
改めてチェックを入れた場合、どのコンポーネントがレンダリングされたかを確認します。

結果は、NavbarとTodoListだけがレンダリングされるようになりました!
Sidebarコンポーネントはもちろん、親のコンポーネントであるAppもレンダリングされていません。
これはコンポーネントの外で、状態管理を行うことにより、独立してデータの受け渡しを行うことができるからです。また、独立させたことによって、テストを書くのが非常に簡単になりますよね!

使用したsignalsの関数

signal(initialValue)
signalのvalueプロパティの値を参照して、値の読み取りや、更新を行う関数。
該当コードはこちらです。

ちなみにsignalの中身はこのような感じです。

このvalueプロパティを介して状態管理を行なっています。

effect(fn)
signalの値を参照し、変更された時に再評価されます。
useEffectと異なり、第二引数に依存配列を定義することなく、自動的に依存関係を管理してくれます。
該当コードはこちらです。

終わりに

いかがだったでしょうか。
preactのsignalsを使用すると、簡単に状態管理を行いつつ、レンダリングを最適化することができたのではないかと思います。現状は、メンテナンス性や拡張性などの問題もあるかもしれませんが、signalsを使用した状態管理が主流になる日も来るかもしれませんね!
最後まで読んでいただき、ありがとうございました。

書いた人はこんな人

ずお(エンジニア)
愛媛県の田舎町で生まれ育ちましたずおと申します。
趣味はサウナと居酒屋巡りです。
未経験で入社させていただいたので、いち早く戦力になれるように日々頑張ります!
座右の銘は「悩んでるひまに、一つでもやりなよ」
ドラえもんの名言です。よろしくお願いします。

関連記事

採用情報

\ あの有名サービスに参画!? /

バックエンドエンジニア

\ クリエイティブの最前線 /

フロントエンドエンジニア

\ 世界を変える…! /

Androidエンジニア

\ みんなが使うアプリを創る /

iOSエンジニア