1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Posted at

はじめに

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


【修正前のコード】
.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>
      )}
    </>
  )
}

解決方法

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>
      )}
    </>
  )
}

参考

おわりに

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


1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?