deve.K

エンジニアが未来を切り開く。

Reactフックでfetchを使用しasync awaitでAPI呼び出し

react fetch

以前の記事でReactフックでFetchメソッドを使用しasync/awaitを使用しないAPI呼び出しの簡単な処理を解説致しました。

React Hooks API fetchで取得 - deve.Kdev-k.hatenablog.com

ですが、基本的にFetchを使用する場合ではasync/awaitの構造体として呼び出していく必要があり、外部リソースではPromiseによって呼び出されるのが一般的です。

本日は、useEffectとuseStateフックでasync/awaitを使用してAPI呼び出しを処理する方法を簡単な例で解説していきます。

Fetchメソッド

fetchはリソースをフェッチするためのシンプルなインターフェースとなります。

Fetchを使用すると、XMLHttpRequest(XHR)よりも簡単にネットワークリクエストとレスポンスを処理できます。

唯一の違いは、Fetch APIがPromisesを使用することです。

これは、 XMLHttpRequest(XHR)が提供するコールバック地獄や定型的な重いコードを回避する方法を提供してくれています。

fetch関数は、フェッチするリソースへのパスである1つの引数を取り、その要求の応答に解決されるPromiseを返します。

Promiseについては下記で解説しております。

dev-k.hatenablog.com

ReactフックでFetchAPIとPromisesの使用

APIエンドポイントからのデータを含むユーザーリストをレンダリングするためにFetchAPIとPromisesを使用する簡単な例で見ていきましょう。

ファイルの先頭にuseEffectをインポートします。

import  { useEffect } from "react"

App関数コンポーネント内でuseEffectフックを定義します。

useEffectフックの第2引数には空の配列を渡します。

const App = () => {

useEffect(() => {

    }, []);

    return <div></div>;
};

ページにアクセスしたら、APIを呼び出します。

つまり、コンポーネントのライフサイクルのマウント部分でAPIを呼び出していきます。

まずurlを定義し、ダミー用で提供されている『https://jsonplaceholder.typicode.com/users 』をuseEffectフック内の変数に保存(代入)します。

const App = () => {

useEffect(() => {

const url = "https://jsonplaceholder.typicode.com/users";

    }, []);

    return <div></div>;
};

async関数の作成

データをfetchするための、async関数を作成していきます。

async関数は、Promiseが解決された後、続行する前に待機する必要がある関数となります。

つまり、関数はデータがPromiseとしてfetch APIで呼び出された後、続行する前に待機する必要があります。

useEffect(() => {
const url = "https://jsonplaceholder.typicode.com/users";

const fetchUsersData = async () => {
try {
const res = await fetch(url);
const json = await res.json();
console.log(json);

} catch (e) {
console.log("error", e);
  }
}

    fetchUsersData(); //呼び出し
}, []);

fetchUsersData関数をuseEffectフック内に入れて呼び出します。

try {
const res = await fetch(url);
const json = await res.json();
console.log(json);
 } catch (e) {
console.log("error", e);
}

fetchUsersData関数はtry...catch文でラップされています、なので関数はエラーをキャッチしてコンソールに表示します。

これはデバッグに役立ち、アプリが予期せずクラッシュするのを防いでくれます。

tryの内部では、JavaScriptの組み込みferch API を使用して、それをresという変数に割り当てます、そしてjsonplaceholder API からユーザーを取得しています。

awaitキーワードを直前に置いて、次の行を実行する前にfetchのタスクが完了するのを待つように関数に指示しています。

responseを取得した後に、.jsonメソッドを使って解析しています。

これは、responseを簡単に読みとれるJSONデータに変換しています、JSONオブジェクトでpromiseを解決します。

つまりjson()メソッドにより、新しいpromiseが返されるため、awaitキーワードを使用して、 asyncキーワードを使用して定義されたasync関数内でpromiseが解決されるのを待つことができます。

json()メソッドを使用する事で、格納されていたjsonファイルのデータを取得することが可能となります。

そして、そのJSONデータをコンソールに出力している事になります。

コンソールの出力はcodepen.io/enjinia_f/demoで確認できます。

全体図コードは下記となります。

const App = () => {
  
useEffect(() => {
const url = "https://jsonplaceholder.typicode.com/users";

const fetchUsersData = async () => {
try {
const res = await fetch(url);
const json = await res.json();
console.log(json);
} catch (e) {
console.log("error", e);
 }
}

fetchUsersData(); //呼び出し
}, [])
  return (
<div></div>
    )
  }

useStateフックを使用する

Appコンポーネントの最上部にuseStateフックを定義します。

const App = () => {

const [users, setUsers] = useState([])

useEffect(() => {
    const url = "https://jsonplaceholder.typicode.com/users";

デフォルトの初期値として、空の配列を渡してあげます。

try {
const res = await fetch(url);
const json = await res.json();
   setUsers(json)
console.log(json);
} catch (e) {
console.log("error", e);
}

try内部にuseStateフックの配列内の状態更新用の関数、2つ目のsetUsers()の引数にjson変数を渡します。

awaitキーワードで待機状態のJSONオブジェクトです。

そしてmap関数を使用してliタグを新しく生成し、取得したユーザーデータの名前をリストとしてブラウザに表示させます。

<div>
<ul>
{users.map((user) => <li key={user.id}> {user.name} </li>)}
</ul>
</div>

つまり、async関数内でPromiseが解決されるのを待機し、GETリクエストを送信した後、返されたデータを繰り返し処理します。

See the Pen React fetch async/await APIからデータ取得 by dev.K | Webアプリ開発者 (@enjinia_f) on CodePen.

条件付きtryブロックのエラー処理

useStateフックで初期値をfalseで設定します。

const App = () => {

const [users, setUsers] = useState([])
const [isError, setIsError] = useState(false);

useEffect(() => {
 const url = "https://jsonplaceholder.typicode.com/users";

次にtry内部でif…else文を使用します。

条件付きtryブロックとなります。

try {
const res = await fetch(url);

if(res.ok){
  const json = await res.json();
  setUsers(json)
}else {
  throw "Erorr"
}

上記では、okプロパティを使用しています。

リクエストが成功したかどうかはResponseオブジェクトのokプロパティで認識可能です。

これは、ステータスコードが200〜299の範囲であればtrueを返し、それ以外のステータスコードfalseを返します。

okプロパティでなく、HTTPステータスコードの200のステータスに対してのみチェックしても構いません。

if(res.status === 200)

これは同じように機能しますが、200のステータスのみのチェックとなりますので、本日はokプロパティを使用しました。

最後に条件付き三項演算子を使用します。

return (
 <div>
{isError ? <h2>Warning: Error! もう一度、お試し下さい。</h2> :
<ul>
 {users.map((user) => <li key={user.id}> {user.name} </li>)}
</ul>
 }
</div>
    );

isErrorは初期値でfalseとなっているので、条件付きtryブロックが正常に実行された場合、コロン( : )の後の2つ目の条件式であるmap関数が実行されます。

See the Pen React fetch async/await 条件付きtryブロック エラー処理 by dev.K | Webアプリ開発者 (@enjinia_f) on CodePen.


okプロパティを否定論理( ! )で反転させて出力してみて下さい。

try {
const res = await fetch(url);

if(!res.ok){ 
  const json = await res.json();
  setUsers(json)
}else {
  throw "Erorr"
}

否定論理で反転された、okプロパティは200〜299ステータスコードで結果はfalseとなります。

See the Pen React async/await 条件付きtryブロック エラーの例 エラー処理 by dev.K | Webアプリ開発者 (@enjinia_f) on CodePen.

これらを、カスタムフックとして再利用する方法は、カスタムフックの基礎に精通する必要があります。

下記で解説しております。

dev-k.hatenablog.com

間違ったFetch

useEffectでデータ取得を行うには、1つだけ間違った方法があります。

これを行うと、他の開発者は悲鳴をあげるでしょう。

useEffect(async () => {
  const data = await fetchData();
}, [fetchData])

上記の問題は、useEffectの最初の引数が、何も返さないundefined(未定義)か、関数を返す(副作用をクリーンアップする)ことになっている場合です。

しかし、async関数はPromiseを返すので、関数として呼び出すことはできません。

これは単純にuseEffectが求めるものではありません。

単にuseEffectフックがその最初の引数として期待するものではありません。

かならず、async関数はuseEffectフック自体の内部に記述して下さい。

useEffect(() => {
 
const fetchData = async () => {
const data = await fetch('https://xxxx.com');

}
fetchData() //呼び出し
.catch(console.error);

  },[])

場合によっては、データ取得関数をuseEffectの外側に置きたい場合があったりします。

そのような場合は、関数をuseCallbackでラップするように注意して下さい。

useCallbackフックの解説は下記で解説しております。

dev-k.hatenablog.com

最後に

promise.all()を使用すれば、並列で待機が可能です。

なるべく、すべてのレンダリングで実行されないように、useEffectフックには常にクリーンアップ関数を提供するように心掛けて下さい。

そして、useEffectをasyncにすることはできません。

useEffect内部でasync関数を使用してください。

APIからデータをフェッチするには、さまざまな方法があります。

あなたが好む方法で、色々と試してみてください。

ですが、useEffectフックとasync関数の扱いには十分気をつけて下さい。

useEffectフックでのasync関数の正しい使い方をご紹介しました。

本日は以上となります。

最後まで読んで頂きありがとうございます。

プライバシーポリシー