副作用の扱いについて

公開日: @fukusan0901

クラスコンポーネントと関数コンポーネントの副作用の扱いの違いが勉強になったのでメモしておきます。詳しい書き方などは、公式ページなどを参照のこと。

副作用とは

公式によると「データの取得、購読 (subscription) の設定、あるいは React コンポーネント内の DOM の手動での変更」などが副作用の例とあります。説明は不要かもしれませんが、その処理が実行されることでその後のコンポーネントの状態を変化させるもののことです。副作用は英語で「side-effect」なので、「useEffect」なんですね。

クラスコンポーネントでの副作用

クラスコンポーネントでは、componentDidMount と componentDidUpdate と componentWillUnmount によって副作用を表現していました。componentDidMount にそのコンポーネントがマウントされた最初の処理を書き、componentDidUpdate にそのコンポーネント内で行う処理を書く、そして componentWillUnmount にそのコンポーネントがアンマウントされるときに行う処理を書くという感じです。

関数コンポーネントでの副作用

関数コンポーネントでは、useEffect フックを使用することでクラスコンポーネントでのライフサイクルメソッドを一つにまとめて書くことができます。書き方などは割愛します。

扱いの違い

上記の通り、クラスコンポーネントのライフサイクルメソッドと、関数コンポーネントの useEffect はやっていることは同じに見えます。ですが実際には細かい違いがあります。下記が主な違いです。

  • 実行されるタイミング
  • props と state の値の反映タイミング
  • 凝集の単位

まず、実行されるタイミングについてです。componentDidMount は コンポーネントがマウントされて、レンダリング内容が仮想 DOM からリアル DOM へ反映される前に、ブラウザへの表示を止めて行われます。そのため、componentDidMount 内に重たい処理が書かれている場合などは、ブラウザに何も表示されない時間があります。その反面 useEffect 内に書かれた処理は、レンダリング内容が仮想 DOM からリアル DOM に反映された直後に実行されます。そのため、一旦プレースホルダなどを表示してここに何かしらが表示されるということを伝えることもできるし、レイアウト崩れを防ぐこともできます。Youtube などを開いたときに空の枠だけ表示されることがあると思いますがあんな感じですね。実際、何も表示されずに待たされるか、プレースホルダだけでも表示されるかという違いは UX の観点で考えてもすごく良さそうです。

次に、props と state の値の反映タイミングについてです。これは少し分かりづらいので誤解を恐れずに噛み砕いて書きます。クラスコンポーネントでは、props や state の参照に this をつけてアクセスすると思います。この場合の state や props は常に最新の値を参照していますが、実際には this にあたる箇所のレンダリングに時間がかかる処理が書かれていたら、その反映に時間がかかり、意図しない this を指定してしまう場合があります。しかし、関数コンポーネントではレンダリングする前に、state や props の値が決まっているため、このような問題は起きません。この反映タイミングの違いは、そのままバグの起こり辛さに繋がります。またこの説明では少し分かりづらい箇所もあると思うので、なんとなくの理解で React 開発チームの方が書いたブログ(上から 3 つめのリンク)を読むと良いと思います。

最後に凝集の単位についてです。これは、それぞれの副作用の項でも書いたのですが、クラスコンポーネントでは一連の処理も、componentDidMount と componentDidUpdate と componentWillUnmount の 3 つに分けて書かなければならないのに対し、関数コンポーネントでは、useEffect 内に一括で書くことができます。これは、コードの見通しに寄与するとともに、カスタムフックなどロジック部分を切り離しやすく、再利用のしやすさにも寄与します。

こんな感じで、一見同じような処理をしているようにみえても、中身は全然違うということがわかり、すごく勉強になりました。ちなみにこちらの記事は 4 つめのリンクの記事を参考に作成しています。

参考