🤡
【React】useEffectとPokéAPI で学ぶ非同期処理
「非同期処理」って?
同期処理とは:
普通のコードは上から順番に1つずつ実行される。
console.log('1');
console.log('2');
console.log('3');
結果:
1
2
3
非同期処理とは:
すぐに終わらないもの(時間がかかるもの)をやってる間に、他の処理を先に進める仕組みのこと。
例:
- サーバーからデータを取ってくる(API通信)
- ファイルを読み込む
- タイマー(setTimeout)
console.log('1');
setTimeout(() => {
console.log('2');
}, 1000);
console.log('3');
結果:
1
3
2(1秒後)
→「setTimeout」が終わるのを待たずに、次(3)が先に実行される。
なぜ非同期処理なのか
例えば、ネットワークからデータを取ってくるとき、数秒~数十秒かかることがあるため、もし同期でやったら画面が固まってしまう。
そのため、以下が重要。
- 非同期にして待つ
- その間にUIの更新や別の処理を進める
JavaScriptの非同期の方法
- コールバック(昔のやり方)
function fetchData(callback: (data: string) => void) {
setTimeout(() => {
callback('データ取得完了!');
}, 1000);
}
fetchData((data) => {
console.log(data);
})
問題点:
- コールバックがネストが深くなりやすい(いわゆるコールバック地獄)
- エラーハンドリングが面倒
- Promise(現在の標準)
function fetchData(): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('データ取得完了!');
}, 1000);
});
}
fetchData().then((data) => {
console.log(data);
});
-
resolve
: 成功したときの処理 -
reject
: 失敗したときの処理
エラー処理:
fetchData()
.then((data) => {
console.log(data);
})
.catch((error) => {
console.error(error);
});
- async / await
async function getData() {
const data = await fetchData();
console.log(data);
}
getData()
ポイント
-
async
を関数につけると、その中でawait
が使える -
await
はPromiseの結果を「待つ」
APIをReactとuseEffectを使って叩いてみる(TypeScript)
PokéAPI
という無料APIからデータを取ってくるとする。
↑こんな感じ
ファイル構造
my-app/
├── src/
├── components/
│ └── PokemonComponent.tsx
├── App.tsx
└── index.tsx
サンプルコード
PokemonComponent.tsx
import React, { useEffect, useState } from 'react';
type Pokemon = {
name: string;
sprites: {
front_default: string;
};
types: {
type: {
name: string;
};
}[];
};
const PokemonComponent = () => {
const [pokemon, setPokemon] = useState<Pokemon | null>(null);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchPokemon = async () => {
console.log('1. データ取得開始');
try {
const res = await fetch('https://pokeapi.co/api/v2/pokemon/pikachu');
console.log('2. レスポンス受け取り完了');
if (!res.ok) throw new Error('取得に失敗しました');
const data: Pokemon = await res.json();
console.log('3. JSON 変換完了');
console.log('4. データの中身:', data);
setPokemon(data);
console.log('5. ステートにセット完了');
} catch (err) {
console.error('エラー発生:', err);
setError((err as Error).message);
}
};
fetchPokemon();
console.log('6. fetchPokemon 呼び出し完了');
}, []);
if (error) return <p>エラー: {error}</p>;
if (!pokemon) return <p>ポケモン読み込み中...</p>;
return (
<div>
<h2>{pokemon.name.toUpperCase()}</h2>
<p>タイプ: {pokemon.types[0].type.name}</p>
<img src={pokemon.sprites.front_default} alt={pokemon.name} />
</div>
);
};
export default PokemonComponent;
setTimeoutを使って、もっと非同期を体感する。
サンプルコード
PokemonComponent.tsx
import React, { useEffect, useState } from 'react';
type Pokemon = {
name: string;
sprites: {
front_default: string;
};
types: {
type: {
name: string;
};
}[];
};
const PokemonComponent = () => {
const [pokemon, setPokemon] = useState<Pokemon | null>(null);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchPokemon = async () => {
console.log('1. データ取得開始');
// 2秒待つ
await new Promise((resolve) => {
console.log('2. 2秒待機開始');
setTimeout(() => {
console.log('3. 2秒経過');
resolve(true);
}, 2000);
});
try {
const res = await fetch('https://pokeapi.co/api/v2/pokemon/pikachu');
console.log('4. レスポンス受け取り完了');
if (!res.ok) throw new Error('取得に失敗しました');
const data: Pokemon = await res.json();
console.log('5. JSON変換完了');
console.log('6. データ:', data);
setPokemon(data);
console.log('7. ステートにセット完了');
} catch (err) {
console.error('エラー:', err);
setError((err as Error).message);
}
};
fetchPokemon();
console.log('8. fetchPokemon呼び出し完了');
}, []);
if (error) return <p>エラー: {error}</p>;
if (!pokemon) return <p>ポケモン読み込み中...</p>;
return (
<div>
<h2>{pokemon.name.toUpperCase()}</h2>
<p>タイプ: {pokemon.types[0].type.name}</p>
<img src={pokemon.sprites.front_default} alt={pokemon.name} />
</div>
);
};
export default PokemonComponent;
参考
参考にさせていただきました🙏
Discussion