thumbnail

【React】useEffect(副作用)の多用はやめよう

2022/10/05

ReactのuseEffect便利ですよね。

自分もよくお世話になっていますが、なまじ便利すぎるがゆえに必要ないケースでもuseEffectが使われているコードが見られます。

関数型プログラミングに影響を受けているReactにおいて、useEffect(副作用)の多用はあまりよくないとされているのですが、その理由を具体例をもとに紹介します。

useEffectを多用した例

useEffectを使わない場合と比較するために、あえてuseEffectを多用した例を紹介します。

↓はcount1の増加に応じてcount2, count3, count4が変化するコードです。

EffectExample.tsx
import React, { useEffect, useState } from 'react'

const EffectExample: React.FC = () => {
  const [count1, setCount1] = useState(0)
  const [count2, setCount2] = useState(0)
  const [count3, setCount3] = useState(0)
  const [count4, setCount4] = useState(0)

  const onClickAdd = () => {
    setCount1((prev) => prev + 1)
  }

  const onClickReset = () => {
    setCount1(0)
  }

  useEffect(() => {
    setCount2(count1 * 2)
  }, [count1])

  useEffect(() => {
    setCount3(count2 * count2)
  }, [count2])

  useEffect(() => {
    setCount4(count2 + count3)
  }, [count2, count3])

  return (
    <div>
      <ul>
        <li>count1: {count1}</li>
        <li>count2: {count2}</li>
        <li>count3: {count3}</li>
        <li>count4: {count4}</li>
      </ul>
      <button onClick={onClickAdd}>ADD</button>
      <button onClick={onClickReset}>RESET</button>
    </div>
  )
}

export default EffectExample

どうでしょうか? ロジック自体はかなりシンプルなはずなのに若干ロジックが追いづらく見えます。

「こんなuseEffectの使い方しないよ」と思われるかもしれませんが、一つのコンポーネントにuseEffectが多くある場合は結構上の例みたいになってる場合が多いです。

useEffectなしの例

今回の例の場合ではわざわざuseEffectを使う必要がありません。

NoEffectExample.tsx
import React, { useState } from 'react'

const NoEffectExample: React.FC = () => {
  const [count1, setCount1] = useState(0)
  const [count2, setCount2] = useState(0)
  const [count3, setCount3] = useState(0)
  const [count4, setCount4] = useState(0)

  const onClickAdd = () => {
    const newCount1 = count1 + 1
    const newCount2 = newCount1 * 2
    const newCount3 = newCount2 * newCount2
    const newCount4 = newCount2 + newCount3

    setCount1(newCount1)
    setCount2(newCount2)
    setCount3(newCount3)
    setCount4(newCount4)
  }

  const onClickReset = () => {
    setCount1(0)
    setCount2(0)
    setCount3(0)
    setCount4(0)
  }

  return (
    <div>
      <ul>
        <li>count1: {count1}</li>
        <li>count2: {count2}</li>
        <li>count3: {count3}</li>
        <li>count4: {count4}</li>
      </ul>
      <button onClick={onClickAdd}>ADD</button>
      <button onClick={onClickReset}>RESET</button>
    </div>
  )
}

export default NoEffectExample

useEffectを多用する場合と比較してcount2, count3, count4の変化が追いやすいと思います。

またcount2, count3, count4変化のタイミングがADDボタンとRESETボタンを押したタイミングであるということがコードからわかりやすいのもポイントです。

まとめ

まとめるとuseEffectを多用するデメリットは以下のとおりです。

  • ロジックが追いづらく、同期的に処理されるわけではないのでバグの温床になりうる
  • 副作用の連鎖により、useEffect発火のタイミングがわかりづらくなる

useEffectを使用する際には、本当にuseEffectを使う必要があるのかな?と考えるといいかもしれません 🤨

author picture

Mitsuru Takahashi

京都市内にてフリーランスエンジニアとして活動しています。

detail

Profile

author picture

Mitsuru Takahashi

京都市内にてフリーランスエンジニアとして活動しています。

detail

© 2022 mitsuru takahashi