ReactでAPIデータを取得して何かをする。という場面が多いので、今後の雛形としてメモしておきます。
住所検索アプリ(サンプル)
サンプルとして、郵便番号から住所を検索できるアプリを作りました。
ユーザーが入力した郵便番号をAPIに送信して、返却された住所をブラウザに表示します。
入力フォームに郵便番号を入力して「住所検索」ボタンをクリックすると、住所が表示されます。
実際の動作は以下からご確認ください!
※コンソールログも合わせてご確認ください。
APIは、株式会社アイビスが運営する配信サービス「zipcloud」を使用させてもらいました。
郵便番号を含むリクエストURLを送ると、住所を含んだデータを返却してくれます。
公式のサイトはこちらです。
コード(全体)
全体のコードはこちらになります。
実際はコンポーネントで分割しますが、サンプルなので1ファイルにまとめています。
App.js
import { useState, useEffect } from "react";
import axios from "axios";
import "./styles.css";
export default function App() {
const [zip, setZip] = useState(""); // 郵便番号(入力)
const [query, setQuery] = useState("");
const [resultTxt, setResultTxt] = useState(""); // 住所
useEffect(() => {
const fetchData = () => {
console.log("データを取得します");
console.log(query);
axios
.get(`https://zipcloud.ibsnet.co.jp/api/search?zipcode=${query}`)
.then((res) => {
console.log(res);
// APIがうまく動作していない時のエラー
if (res.status !== 200) {
throw new Error("APIがうまく動作していないようです");
} else {
// 郵便番号の桁数が不正の場合のメッセージ
if (res.data.message) {
setResultTxt(res.data.message);
return;
}
// 郵便番号が存在しない場合のエラーメッセージ
if (res.data.results == null) {
setResultTxt("郵便番号が見つかりませんでした");
return;
}
// 取得した住所を格納
let getAddress = res.data.results[0];
setResultTxt(
`〒${getAddress.zipcode}\n${getAddress.address1}${getAddress.address2}${getAddress.address3}`
);
}
})
.catch((err) =>
setResultTxt(`データがうまく取得できませんでした。${err}`)
);
};
if (query) fetchData(); // 郵便番号が入力されてたら実行
}, [query]); /// zipの値が更新されたら実行
// 住所検索をクリックした時
const onClickGetArea = () => {
console.log("住所検索をクリックしました");
// 未入力だったらアラートを表示
if (zip === "") {
alert("郵便番号を入力してください");
return;
}
// データ取得
setQuery(zip);
};
const inputStyle = {
border: "1px solid #ccc",
padding: "5px 10px",
borderRadius: "4px",
marginRight: "10px"
};
const h1Style = {
fontSize: "1.2em",
color: "#b09851",
background: "#e9e1c8",
padding: "5px 10px"
};
return (
<div>
<h1 style={h1Style}>住所検索サンプル</h1>
<p>
郵便番号を入力して「住所検索」ボタンをクリックしてください
<br />
<span style={{ fontSize: ".8em" }}>例:1050011</span>
</p>
<input
style={inputStyle}
type="text"
value={zip}
placeholder="郵便番号を入力してください"
onChange={(e) => setZip(e.target.value)}
/>
<button onClick={onClickGetArea}>住所検索</button>
<p>{resultTxt}</p>
</div>
);
}
ポイントとなる点
忘れそうなので、ポイントとなるところをメモしておきます!
ハマったポイントは2つだけです!
- useEffectでAPIにアクセスするタイミングを制御する
- useStateで入力した値を都度更新する
APIデータを取得する(axios)
APIデータを取得するのにaxios(アクシオス)を導入します。
fetchでも良いのですが、データを非同期で取得してくれるし、コードがシンプルにかけるのでこちらを使用します。
次のコマンドでインストールしておきます。
$ npm install axios --save
インストールしたら、axiosを使うファイルにimportしておきます。
import axios from "axios";
axiosの構文
構文は次の通りです。このように直感的に記述できます!
then()をつなげて、コードの実行順を制御することもできます。
axios
.get(<APIのURL>)
.then((res) => {
// データが取得できた時の処理
console.log(res); // resに取得データがオブジェクトで格納される
// 例外エラーを発生させる
throw new Error("<表示させるエラー>");
})
// データが取得できなかった時の処理
.catch((err) => alert("データがうまく取得できませんでした"))
// 最終的に実行される処理
.finally(() => {console.log('取得完了!')});
「zipcloud」から郵便番号を取得するには、次のURLをAPIに送信する必要があります。
https://zipcloud.ibsnet.co.jp/api/search?zipcode=<郵便番号>
APIから住所を取得するには、フォームに入力された郵便番号を、上記のURLに当てはめる必要があります。郵便番号はuseStateで管理します。
フォームに入力された値を更新する(useState)
フォームに入力した値はuseStateで管理して、入力したら都度更新されるようにします。
以下は入力した値を表示する簡単なサンプルです。
import { useState } from "react";
import "./styles.css";
export default function App() {
const [zip, setZip] = useState(""); // 郵便番号(入力)
return (
<div>
<p>郵便番号を入力してください</p>
<input onChange={(e) => setZip(e.target.value)} />
<p>入力結果:{zip}</p>
</div>
);
}
onChangeイベントで、文字が入力されるたびにフォームの値をsetZip()の引数にセットします。
※初期値は空の文字列にしておきます。
そうすることで、変数zipに入力結果(ここでは郵便番号)が格納されます。
実際の動きはこちらで確認できます!
これで入力された値(郵便番号)を取得することができました!
「住所検索」をクリックしたらAPIからデータを取得する(useEffect)
「住所検索」をクリックした時に、APIにデータを送信します。
コンソールでログを確認したところ、なんとそれ以外でも通信が発生していました。
読み込んだ時と、文字を入力した時に発生しています。
読み込んだ時はなんとなく分かるのですが、文字を入力しても通信が発生しています。
これは副作用と呼ばれる現象で、関連するコンポーネントも連鎖して再レンダリングされる仕組みから発生します。
今回、入力した値はuseState(変数zip)で管理しており、APIに送信するURLに組み込んでいます。入力するごとにStateは更新され、関連するURLも更新されデータ通信が発生しています。
useEffectで副作用を制御する
useEffectを使って、「住所検索」をクリックした時だけAPIにアクセスするように制御します。
構文は以下の通りです。
// DOMをレンダリングした後に実行
useEffect(() => {
// 処理する内容
}, []); // 依存する変数を配列で指定。空の場合、一度だけ実行
ポイントは、第二引数に、依存する変数を設定することです。
こうすることで、設定した変数に更新があった時、useEffectに記述した処理が実行されます。
なお、第二引数の値の更新に関係なく、最初の1回だけは必ず処理が実行されます。
以下は、入力した時だけイベントを発生させる簡単なサンプルです。
先程の例と異なり、「住所検索」をクリックした時だけ、通信が発生することを確認できました。
useEffectが依存している変数は、入力した郵便番号(zip)ではなく、queryです。この値もuseStateで管理しており、「住所検索」をクリックするごとに更新されます。
queryにzipをセットすることで、「住所検索」をクリックした時点の郵便番号が引き渡されることになります。
※リアルタイムで住所を表示させる場合、依存変数をzipにするのもありかもしれません。。
これで「住所検索」をクリックしたら、APIに住所を送信する仕組みが完成しました!
なお、useEffectは読み込み時に実行されるので、queryに値があるだけfetchData()を実行するようにしました。
APIから取得した住所をブラウザに表示する(useState)
APIから取得した値をブラウザに表示します。
以下は「住所検索」をクリックしたら、住所を表示する簡単なサンプルです。
通信は発生せず、以下のサンプルデータを表示しているだけです。
// APIで取得されるデータを想定
const res = {
data: {
message: null,
results: [
{
address1: "東京都",
address2: "港区",
address3: "芝公園",
kana1: "トウキョウト",
kana2: "ミナトク",
kana3: "シバコウエン",
prefcode: "13",
zipcode: "1050011"
}
],
status: 200
}
};
特にポイントとなるところはありませんが、改めてログを確認すると、「住所検索」をクリックした後にuseEffectの処理が実行されているのが分かります。
まとめ
useEffectは副作用を制御できますが、意味を理解するのに時間がかかりました。。
APIからデータを取得する際、必ず成功するわけではないので、エラー処理を実装するのも難しかったです。
これで雛形ができたので、以降はコピペでいけるはず、、、。変なところがあったら都度修正します!
コメント