Hero img
React Suspense

React-Suspenseを使ってみる

Reactでfetch後のレンダリングをする事は今や当たり前に使用する方法でuseeffectを使ってフェッチ後のレンダリングをする、しないを判定するのがあるが、Suspenseで再レンダリングや、パフォーマンス的にもいいらしい。


目次

  • React Suspense
  • 今まで
  • Suspense使って
  • 実際のAPIでは
  • Suspenseを使うべき理由
  • まとめ

React Suspense

React 16...正式に18でSuspenseが使用できるようになった。このSuspenseとは非同期・同期処理で、apiフェッチや、大きいファイルの読み込み遅延が発生してしまい、レンダリングするデータがまだ用意できない状態時に使用する。

今まで

今までフェッチでデータ取得後そのデータを加工、レンダリングする為にはuseeffectを使って、useStateのデータが無ければLoading...を返し、データが取得できたらレンダリングするコードはこんな感じだと思うが

   import React, { useState, useEffect } from 'react';
function App() {
  const [producdt, setProducdt] = useState<any[]>([]);
  useEffect(() => {
    Fetchobjecttest().then(data => {
      setProducdt(data);
    })
  }, [])
  if(producdt.length === 0) return (<p>Loading...</p>)
  return (
    <div>
      <p>テスト</p>
      {producdt.map((item, index) => (
      <p key={index}>{item.name}</p>
  ))}
    </div>
    )
}
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
const Fetchobjecttest = async () => {//データを取得するfetchAPI
  await sleep(5000); // Delay for 5 seconds
  let obje = [
    {
      "name": "BBBBB",
    },
    {
      "name": "AAAAAA",
    },
    {
      "name": "CCCCCC",
    },
    {
      "name": "DDDDDD",
    },
    {
      "name": "EEEEEE",
    },
    {
      "name": "FFFFFF",
    },
    {
      "name": "GGGGGG",
    }
  ];

  return obje;
}
export default App;

suspense-useeffect-b suspense-useeffect-a

suspenseを使うと


import React, {Suspense } from 'react';
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
const Fetchobjecttest = async () => {
  await sleep(5000); // Delay for 5 seconds
  let obje = [
    {
      "name": "BBBBB",
    },
    {
      "name": "AAAAAA",
    },
    {
      "name": "CCCCCC",
    },
    {
      "name": "DDDDDD",
    },
    {
      "name": "EEEEEE",
    },
    {
      "name": "FFFFFF",
    },
    {
      "name": "GGGGGG",
    }
  ];

  return obje;
}
const Suspensfetch = () => {
  let status = "pending"
  let result: any;
  console.log("FETCHING")
  let fetching = Fetchobjecttest()
    .then((success) => {
      status = "fulfilled";
      result = success;
    })
    // Fetch request has failed
    .catch((error) => {
      status = "rejected";
      result = error;
    });
  return () => {
    if (status === "pending") {
      throw fetching; // Suspend(A way to tell React data is still fetching)
    } else if (status === "rejected") {
      throw result; // Result is an error
    } else if (status === "fulfilled") {
      return result; // Result is a fulfilled promise
    }
  };
}
const Postdemo = Suspensfetch();
const FetchandRender = () => {
  const products = Postdemo();
  return (
    <>
      {products.map((item: any, index: number) => (
        <p key={index}>{item.name}</p>
      ))}
    </>
  )
}

function App() {
  return (
    <div>
      <p>テスト</p>
      <Suspense fallback={<p>Loading...</p>}>
        <FetchandRender />
      </Suspense>
    </div>
  );
}

export default App;

Suspense使って

まぁコードが長くなってしまいましたね...
でもでもパフォーマンスは良いらしい、検証とかしてないけど...

複数個所のデータを取得する際は並列処理でパフォーマンスは良くなる
フェッチのデータもキャッシュ?して残ってたのでいいのかも知らんけど?

実際のAPIでは

上記のコードではサンプルのデータを使いましたが、実際使うのはこんな感じかな?

function SusPnesefetch(){
  let status = "pending"
  let result:any;
  console.log("FETCHING")
  let fetching = fetch("/api/demoobject")//<--こんな感じ
  .then((res) => res.json())//<-res.jsonは使うよね?たぶん
  .then((success) => {
    status = "fulfilled";

    result = success;
  })
    .catch((error) => {
      status = "rejected";
      result = error;
    });
  return () => {
    if (status === "pending") {
      throw fetching; 
    } else if (status === "rejected") {
      throw result; 
    } else if (status === "fulfilled") {
      return result; 
    }
  };
}

Suspenseを使うべき理由

個人的意見として、結局のところ可視性と、安定性だと思う。useEffectsを使うと再レンダリング制御、読み込み速度(ウォーターフォール)の問題を解決しなければならないが、
フェッチ回数が多い、遅延の振り幅が大きいとかなってくるとuseEffectsだけでは制御するのはかなり難しい。「Promiseとかでできるのかな?」

まとめ

Reactを使って今回のようにデータを取得して、レンダリングする際、useeffectを使うのは ??!。
なんでuseEffectを使わないといけないの?もっとあるだろうと思ってましたが、ようやく正式にsuspenseがリリースされたので納得はしているが、ちょっとコード長くないか?とは思う。
wapper作って置いたら汎用的に使えていいと思うし、データを取得する所が1ヶ所ではなく、複数個所、複数回fetchする際はuseEffectを使うのなかなか見づらいコードになり、再レンダリング制御とか、かなり気にかけないといけないので、今後は積極的にこのsuspenseを使って言った方がいいと感じる。
そもそもデータフェッチのためにuseEffectを使うのは今後ダメなコードになると思う。

関連記事

コメント

コメントを書く

質問や、間違いがありましたら、お気軽にどうぞ

※お名前とコメントの内容を入力してください。
※全てこのブログ上に公表されます。
※許可なく管理者によって修正・削除する場合がございます。 詳しくはプライバシーポリシーを参照ください