作成日:2023-11-22
Reactでfetch後のレンダリングをする事は今や当たり前に使用する方法でuseeffectを使ってフェッチ後のレンダリングをする、しないを判定するのがあるが、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;
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;
まぁコードが長くなってしまいましたね...
でもでもパフォーマンスは良いらしい、検証とかしてないけど...
複数個所のデータを取得する際は並列処理でパフォーマンスは良くなる
フェッチのデータもキャッシュ?して残ってたのでいいのかも知らんけど?
上記のコードではサンプルのデータを使いましたが、実際使うのはこんな感じかな?
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;
}
};
}
個人的意見として、結局のところ可視性と、安定性だと思う。useEffectsを使うと再レンダリング制御、読み込み速度(ウォーターフォール)の問題を解決しなければならないが、
フェッチ回数が多い、遅延の振り幅が大きいとかなってくるとuseEffectsだけでは制御するのはかなり難しい。「Promiseとかでできるのかな?」
Reactを使って今回のようにデータを取得して、レンダリングする際、useeffectを使うのは ??!。
なんでuseEffectを使わないといけないの?もっとあるだろうと思ってましたが、ようやく正式にsuspenseがリリースされたので納得はしているが、ちょっとコード長くないか?とは思う。
wapper作って置いたら汎用的に使えていいと思うし、データを取得する所が1ヶ所ではなく、複数個所、複数回fetchする際はuseEffectを使うのなかなか見づらいコードになり、再レンダリング制御とか、かなり気にかけないといけないので、今後は積極的にこのsuspenseを使って言った方がいいと感じる。
そもそもデータフェッチのためにuseEffectを使うのは今後ダメなコードになると思う。
質問や、間違いがありましたら、お気軽にどうぞ
※お名前とコメントの内容を入力してください。
※全てこのブログ上に公表されます。
※許可なく管理者によって修正・削除する場合がございます。 詳しくはプライバシーポリシーを参照ください