🐽

Next.js(React)のuseEffectの無限ループの解決方法、そしてアンチパターン

2022/06/28に公開

Next.jsをはじめとしたリアクティブなフロントエンドの実装は経験がしたことがなく、 useEffectが無限ループする現象にハマりました。 今回はその解決策をまとめます。

前提

開発環境は以下の表ようになります。

ソフトウェア、アプリ バージョン 確認方法
docker Docker version 20.10.14 コマンドで確認
Node node:14.17.0 Dockerfileで確認
Next.js "next": "12.1.4" package.json
yarn "yarn": "1.22.5” yarn -v で確認
typescript "typescript": "4.6.3" package.json

useEffectの実行タイミング

useEffectの第2引数に何も指定しなければ、ブラウザの要素のイベントが発火するたびに実行することになる。

しかし、第2引数に変数を指定すると発火するタイミングはstateの変更で検知できるようになり、指定した引数の変更が行われるたびにuseEffectが発火するようになります。

https://ja.reactjs.org/docs/hooks-effect.html#tip-use-multiple-effects-to-separate-concerns

解決策)useEffectの第2引数を利用する

useEffectの第2引数を発火のタイミングをコントロールできるので、これを利用します。
以下の例ではcountry, price, shopAreaを第2引数に指定しています。

それらの変数をstateとして持つようにしています。

const Home: NextPage = () => {
  const [country, setCountry] = useState(0);
  const [price, setPrice] = useState(0);
  const [shopArea, setShopArea] = useState(0);
  const [coffeeShops, setCoffeeShops] = useState(coffeeShopList);
  const fetchCoffeeShops = async(country:number, price:number, shopArea:number) => {
    const res = await fetch(`/api/coffeeshops?country=${country}&price=${price}&shopArea=${shopArea}`);
    const data = await res.json();
    setCoffeeShops(data);
  };
   useEffect(() => {
    fetchCoffeeShops(country, price, shopArea);
  }, [country, price, shopArea]);
  return (
    <>
			...
    </>
  )
}

export default Home;

これのような実装にするとこんなアプリができます。↓

https://whaleshark.vercel.app/

動くけど、このやり方はアンチパターン

以上の方法で実装はしましたけど、他の記事ではアンチパターンとして解説されていました。

ぼくには何がどういう理由でアンチパターンなのかわかりませんけど、勉強していくなかで理解していくつもりです。

https://zenn.dev/dai_shi/articles/dee54f995e6e74

間違った実装)if文で制御する

useEffectの第2引数を利用しないで実装しようとして、stateの変更をif文で制御しようとしてもうまくいきませんでした。

なので経験としてif文の評価タイミングと制御をするのはアンチパターンと言ってもいいでしょう。

useEffectが発火するのはHTMLの変更タイミングで、useEffectが発火→HTMLのレンダリング→それにたいするuseEffectが発火、というような無限ループになってしまいます。
(もしかするとうまく制御することができるかもしれません。ただけどコードの可読性を下げることになり良くないです。)

参考URL

https://ja.reactjs.org/docs/hooks-reference.html
https://qiita.com/k-penguin-sato/items/9373d87c57da3b74a9e6
https://qiita.com/takano-h/items/11f7b35dbf4844f7565d
https://techblg.app/articles/how-to-use-await-fetch-in-use-effect/
https://tyotto-good.com/blog/usestate-basic
https://yutaro-blog.net/2021/09/15/react-hooks/#useeffect

Oriental Coffee Ventures

Discussion