【React】Jotaiで状態管理中、無限ループが発生した場合の解決方法について

# はじめに
開発中、Loading...を画面に表示させる実装中、レンダリングの無限ループが発生したため、こちらの解決方法を記事にします。

<br>

<details><summary>【修正前のコード】</summary>

```.tsx
import { atom, useAtom } from 'jotai';
import { latitudeAtom, loadingAtom, longitudeAtom } from './Atom';
import { useNavigate } from 'react-router';

export const GeolocationFetch = () => {
  const navigate = useNavigate();
  const loadingAtom = atom<boolean>(false); // ← ここが原因でした

  const [latitude, setLatitude] = useAtom<number | null>(latitudeAtom);
  const [longitude, setLongitude] = useAtom<number | null>(longitudeAtom);
  const [loading, setLoading] = useAtom(loadingAtom);

  const onClickFetchGeoLocation = () => {
    navigator.geolocation.getCurrentPosition(successCallback, errorCallback);
    setLoading(true);
  }

  const successCallback = (position: GeolocationPosition) => {
    setLatitude(position.coords.latitude); // 緯度取得
    setLongitude(position.coords.longitude); // 経度取得
    navigate('/mapPage');
    setLoading(false);
  };

  const errorCallback = (error: GeolocationPositionError) => {
    setLoading(false);
    alert('位置情報を取得できませんでした');
  };

  return (
    <>
      {loading ? (
        <div
        className='flex flex-col text-xl font-bold items-center'
        >Loading...</div>
      ) : (
        <div
          className='flex flex-col items-center justify-center'
        >
          <div
            className='flex flex-col items-center'
          >
            <div className='text-xl font-bold'>GeolocationFetchページ</div>
            <button
              className='py-1 px-5 bg-sky-500 rounded-2xl font-black border text-white'
              onClick={onClickFetchGeoLocation}
            >位置情報を取得後画面遷移</button>
          </div>
        </div>
      )}
    </>
  )
}

```
</details>


# 解決方法
atomをstoreで管理する または　memo化する　または useStateで管理する

【store管理ver】
```.tsx
import { atom } from "jotai";

export const loadingAtom = atom<boolean>(false);
```


```.tsx
import { atom, useAtom } from 'jotai';
import { latitudeAtom, loadingAtom, longitudeAtom } from './Atom';
import { useNavigate } from 'react-router';

export const GeolocationFetch = () => {
  const navigate = useNavigate();

  const [latitude, setLatitude] = useAtom<number | null>(latitudeAtom);
  const [longitude, setLongitude] = useAtom<number | null>(longitudeAtom);
  const [loading, setLoading] = useAtom(loadingAtom);

  const onClickFetchGeoLocation = () => {
    navigator.geolocation.getCurrentPosition(successCallback, errorCallback);
    setLoading(true);
  }

  const successCallback = (position: GeolocationPosition) => {
    setLatitude(position.coords.latitude); // 緯度取得
    setLongitude(position.coords.longitude); // 経度取得
    navigate('/mapPage');
    setLoading(false);
  };

  const errorCallback = (error: GeolocationPositionError) => {
    setLoading(false);
    alert('位置情報を取得できませんでした');
  };

  return (
    <>
      {loading ? (
        <div
        className='flex flex-col text-xl font-bold items-center'
        >Loading...</div>
      ) : (
        <div
          className='flex flex-col items-center justify-center'
        >
          <div
            className='flex flex-col items-center'
          >
            <div className='text-xl font-bold'>GeolocationFetchページ</div>
            <button
              className='py-1 px-5 bg-sky-500 rounded-2xl font-black border text-white'
              onClick={onClickFetchGeoLocation}
            >位置情報を取得後画面遷移</button>
          </div>
        </div>
      )}
    </>
  )
}

```

【memo化ver】

```.tsx
import { atom, useAtom } from 'jotai';
import { latitudeAtom, longitudeAtom } from './Atom';
import { useNavigate } from 'react-router';
import { useMemo } from 'react';

export const GeolocationFetch = () => {
  const navigate = useNavigate();
  const loadingAtom =  useMemo(() => atom<boolean>(false), []);

  const [latitude, setLatitude] = useAtom<number | null>(latitudeAtom);
  const [longitude, setLongitude] = useAtom<number | null>(longitudeAtom);
  const [loading, setLoading] = useAtom(loadingAtom);

  const onClickFetchGeoLocation = () => {
    navigator.geolocation.getCurrentPosition(successCallback, errorCallback);
    setLoading(true);
  }

  const successCallback = (position: GeolocationPosition) => {
    setLatitude(position.coords.latitude); // 緯度取得
    setLongitude(position.coords.longitude); // 経度取得
    navigate('/mapPage');
    setLoading(false);
  };

  const errorCallback = (error: GeolocationPositionError) => {
    setLoading(false);
    alert('位置情報を取得できませんでした');
  };

  return (
    <>
      {loading ? (
        <div
        className='flex flex-col text-xl font-bold items-center'
        >Loading...</div>
      ) : (
        <div
          className='flex flex-col items-center justify-center'
        >
          <div
            className='flex flex-col items-center'
          >
            <div className='text-xl font-bold'>GeolocationFetchページ</div>
            <button
              className='py-1 px-5 bg-sky-500 rounded-2xl font-black border text-white'
              onClick={onClickFetchGeoLocation}
            >位置情報を取得後画面遷移</button>
          </div>
        </div>
      )}
    </>
  )
}

```

# 参考
https://zenn.dev/akineko/articles/662bf324a10c82

# おわりに
jotaiを使っていて初めての症状だったため、勉強になりました。


<br>

[<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3707255/8bfacf61-7d8d-6d27-cb07-98d1f8f27fcd.png"  width=100%>](https://projisou.jp)