📚
備忘録: Reactの状態管理ライブライ"jotai"の非同期処理をCSRでもSSRでも問題なく利用できるようにする。
背景
jotaiでREST APIなど非同期のデータをfetchして利用する際、SSRの設定をしていると下記のエラーが発生する。
Error: Text content does not match server-rendered HTML.
useEffectなど駆使してレンダリング後に取得データをsetStateすれば解決するがすごく違和感あるからuseAtomしたらそのまま利用できるようにしたい。
一応公式でもAsyncの対応やSSRの対応についても言及しているが、どれもうまくいかなかった。
諦めてCSRで実装したがそうするとatomWithRefreshが利用できず、ステートの更新に苦心する。
もう自分で理想のAtomを実装するしかない。
というわけで早速結論です。
結論
データの読み込み時のステータス管理はloadableを参考に実装。
import { atom } from "jotai";
// 処理の状態を表す汎用的な型
type AsyncStatus<T> = {
state: "loading" | "hasData" | "hasError";
data?: T;
error?: Error;
};
// 初期状態を設定するための関数
function createInitialState<T>(): AsyncStatus<T> {
return {
state: "loading",
data: undefined,
error: undefined,
};
}
// 汎用的なアトムを作成する関数
export const asyncAtom = <T>(fetchFunction: () => Promise<T>) => {
const initialState = createInitialState<T>();
const dataAtom = atom<AsyncStatus<T>>(initialState);
const refreshAtom = atom(
(get) => get(dataAtom),
async (_, set) => {
set(dataAtom, { state: "loading", data: undefined, error: undefined });
try {
const data = await fetchFunction();
set(dataAtom, {
state: "hasData",
data: data,
error: undefined,
});
} catch (error) {
set(dataAtom, { state: "hasError", data: undefined, error });
}
}
);
return refreshAtom;
};
使い方は下記
// 適当にAPIからデータを取得する処理
const fetchData = async () => {
const response = await fetch("https://api.example.com/data");
if (!response.ok) {
throw new Error("データの取得に失敗しました");
}
return response.json();
};
// Atomを定義
const dataAtom = asyncAtom(fetchData);
// Atomを利用してUIを表示
const SampleComponent: React.FC = () => {
const [data, refresh] = useAtom(asyncAtom(fetchSampleData));
React.useEffect(() => {
refresh();
}, [refresh]);
return (
<div>
{data.state === "loading" && <p>Loading...</p>}
{data.state === "hasError" && <p>Error: {status.error?.message}</p>}
{data.state === "hasData" && (
<pre>{JSON.stringify(data.data, null, 2)}</pre>
)}
<button onClick={refresh}>Refresh</button>
</div>
);
};
非常にシンプルにjotaiで非同期処理を扱えるようになったと思う。
特にログイン中のユーザデータを扱う時に利用したいと考えている。
FlutterのRiverpodだとこのあたりあまり意識してなかったが、SSRはまた違った考慮項目があって勉強になった。
最後に
もっと良い方法などあればぜひ教えてください!
Discussion