🧭

[覚書] Reactを業務で使い始めて知ったこと

2022/06/25に公開

私は、これまでプライベートでしか React を使っていませんでした。
最近、業務で React を使う機会が増えたので、学んだことを残そうと思います。

React の歴史

なんで React って生まれたんだろうって気になりました。
簡単ですが、ちょこっとだけ調べて、次の記事にまとめました。

React は、次の問題を解決したかったんだと思います。

  • DOM ツリーが大きくなるにつれて、下位の変更によるカスケード更新の負荷が大きくなる

そこで、React は、この問題を解決するために、仮想 DOM という仕組みを作ったんだと思います。

仮想 DOM、差分検出処理、そして Fiber

React は、直接 DOM を操作するのではなく、仮想 DOM に対して操作します。仮想 DOM は、名前の通り仮想的な DOM です。
仮想 DOM を DOM へ反映するために、差分検出処理(reconciliation)というアルゴリズムがあったり、Fiber と呼ばれる、レンダリングの最適化(優先順位)を目的としたアルゴリズムもあるようです。これらのおかげで、レンダリング負荷が軽減されるんだと思います。(しらんけど)

まだまだ理解が浅いので、これからもっと学んでいきたいと思います。

レンダリングのタイミングは、いつなんでしょうか。

レンダリングタイミング

基本的に、React は、親コンポーネントをレンダリングすると、子コンポーネントもレンダリングされます。

再レンダリングをキューイングする関数、setState や forUpdate などを呼ぶと、コンポーネントはレンダリングされることになります。

コードベースが大きくなるにつれて、レンダリングのパフォーマンスが悪化していきます。
そこで、パフォーマンスの最適化が求められます。

パフォーマンス最適化

最初からパフォーマンス最適化をする必要はありませんが、要件によっては必要になることもあります。
最適化の手段として、React にある、次の 3 つの関数が使えます。

  • memo
    • コンポーネントのレンダーをスキップできる
      • 以前の props と現在の props で変更がなければ
  • useMemo
  • useCallback
    • 関数をメモ化できる
      • memo と併用して使う

パフォーマンス最適化 – Reactも参考になります。

比較アルゴリズム

React では、コンポーネントや状態が変更されたかどうかの判定に、Object.is() を使っているようです。
Object.is のサンプルコードは、次のとおりです。

Object.is("foo", "foo"); // true
Object.is("foo", "bar"); // false
Object.is([], []); // false

var foo = { a: 1 };
var bar = { a: 1 };
Object.is(foo, foo); // true
Object.is(foo, bar); // false

string や integer のようなプリミティブな値は良いのですが、非プリミティブな値(Object)の場合の考慮が必要です。
例えば、memoの場合は、第二引数に比較関数を渡すことができます。
例えば、次のような感じです。

function MyComponent(props) {}
function areEqual(prevProps, nextProps) {
  return JSON.stringify(prevProps.foo) === JSON.stringify(nextProps.foo);
}
export default React.memo(MyComponent, areEqual);

公式ページにも書いていますが、パフォーマンス最適化のみに使いましょう。

Tips

Object.is() を使われている影響で、非プリミティブな値の状態更新に、工夫が必要です。

const [items, setItems] = useState(["a", "b"]);

// NG
items.push("c");
setItems(items); // 変更されない(Object.is()→true)

// OK
const newItems = [...items, "c"];
setItems(newItems); // 変更される(Object.is()→false)

NG の方は、同じオブジェクトを使いまわしているのに対し、OK の方は、新しくオブジェクトを生成しています。

パフォーマンス調査

トップダウンでパフォーマンス調査をするのが、ベターと思います。

  1. Chrome Developer Tools > Lighthouse を使い、performance score を確認
  2. Chrome Developer Tools > Performance を使い、処理に時間がかかっている箇所を見つける
  3. React Developer Tools > Profiler を使い、React コンポーネントのレンダリングで時間がかかっている箇所を調査

React コンポーネント デザインパターン

React でコンポーネントを実装していると、次の 3 つのパターンがあるようです。

  • Container and presentation
    • ロジックと UI を分離
    • XxxContainer, Xxx という命名が多い
  • Higher order component
    • 高階コンポーネント
    • withXxx という命名が多い
  • Function as child
    • コンポーネントではなく関数を child として渡す

ロジックを独自フックとして切り出す

テスタビリティや再利用性の観点より、ロジックを hooks として切り出すのが良さそうです。

命名は、use から始まることが多いです。

その他

  • コンポーネントコードと同じフォルダ内に、次のファイルを置きたい
    • テストコード (test)
      • 仕様を知る
    • カタログコード(storybook)
      • UI を見る
    • スタイルコード (scss)
  • input 要素などの onChange には、debounce を使う
    • onChange の処理が重たいときに

Discussion