APIレスポンスの扱いが正直怪しい人集合!!
投稿日: 2024年12月04日
JS最初の山であろう非同期処理ですが、あのあたりの処理がいつも決まった書き方してるけどよくわからずに書いているんだよね実はっていう方に向けて解説します~!
分割代入にも少し触れます!
こんな処理よくやりますよね??
const [data, setData] = useState<Post[]>();
const [isLoading, setLoading] = useState(true);
useEffect(() => {
const fetcher = async () => {
const resp = await fetch("/api/posts/", { method: "GET" });
const { posts }: { posts: Post[] } = await resp.json();
setData(posts);
setLoading(false);
};
fetcher();
}, []);
ここイマイチ理解していないよって方に向けてます!
エラーハンドリング(try-catch)省略していますが、実際はすべきです!
APIリクエストは失敗する可能性があるので、エラーが発生した時に処理が止まってしまわないようにエラーはキャッチするようにする必要があります。
念のため、Postの定義はこちらです。
export interface Post {
id: number;
title: string;
thumbnailImageKey: string;
createdAt: Date;
updatedAt: Date;
content: string;
postCategories: PostCategory[];
}
export interface PostCategory {
id: number;
postId: number;
categoryId: number;
createdAt: Date;
updatedAt: Date;
post: Post;
category: Category;
}
export interface Category {
id: number;
name: String;
createdAt: Date;
updatedAt: Date;
posts: PostCategory[];
}
const [data, setData] = useState<Post[]>();
const [isLoading, setLoading] = useState(true);
取得したデータを管理するためのdata、読込み状況の管理のためのisLoadingです。
dataはPost[]、isLoadingはbooleanです。
次にuseEffectです。
下記の記事で解説している初回レンダリング時だけ処理実行のパターンです。
const fetcher = async () => {
const resp = await fetch("/api/posts/", { method: "GET" });
const { posts }: { posts: Post[] } = await resp.json();
setData(posts);
setLoading(false);
};
一行ずつ見ていきます。
エンドポイント/api/posts/にGETリクエストを行います。fetchは非同期処理なのでawaitが前につきます。
const resp = await fetch("/api/posts/", { method: "GET" });
awaitを使用する関数はasyncを付けないといけないです!
GETリクエストの場合、メソッドは省略が可能です。下記でもOK!
const resp = await fetch("/api/posts/");
次に、データを変換します。
const { posts }: { posts: Post[] } = await resp.json();
レスポンスが入っているresp
はResponse
オブジェクトで、その中のボディはストリーム形式です。
そのままでは必要なデータを扱えないという問題があります。そこで、resp.json()
を行うことでjson形式のデータをJSで操作可能なオブジェクトに変換してオブジェクトとして扱えるようにします。
ザックリ、文字列みたいになってて、ほしいデータにアクセスできる状態じゃないからオブジェクトに変換するみたいなイメージで良いと思います・・!
これも非同期処理なのでawaitが付きます。
ストリームとは
データを順序立てて連続的に送受信する手法のことです。これにより大量のデータを効率的に扱うことができるみたいです。(知らんやったので調べました笑)
型を指定しないでanyのままでもいいとは思いますが、この後Posts型のステートに値をセットするので、型は一致させておいた方がいいのではと個人的には思います。
ちなみにあれ??ってなってる方を結構お見かけしますのであえてお伝えしますと、分割代入しています。
分割代入をしない場合の書き方例は下記のような感じです。
const data = await resp.json();
const posts = data.posts;
{posts:[…]}
このようなオブジェクトからpostsを直接代入しているというかんじです。
分割代入は波カッコ{}で囲ってオブジェクトのプロパティ名を直接指定しすることで直接プロパティの値を代入する記法です。よく使うので押さえておきたいところです!
setData(posts);
setLoading(false);
更新関数を使ってステートに値を定義します。
postsの型がPosts[]で、ステートdataもPosts[]なので型一致しててちゃんと入ります〇
isLoadingもfalseに更新することで
if (isLoading) return <div>読み込み中...</div>;
ここでリターンされずに次の処理にすすみます。
最後に
fetcher();
で関数実行することで、初回レンダリング時にのみデータ取得することが出来ます!
初心者向けな内容でしたが、なんとなくサンプルコードコピペしてるから理解が怪しいと実は思ってる方の確認に使えたらいいなと思います🎵