はじめに
ReactのHooksAPIを学習したので、
useEffectとuseReducerを用いて非同期で取得したデータを表示してみる。
ソースコードと解説
import React, { useReducer, useState, useEffect } from "react";
import axios from "axios";
// stateの初期値を定義しておく
const initialState = {
isError: false,
data: {},
};
// Reducerを定義
// アクションは
// {type: 'FETCH_SUCCESS', payload: {name: aaa}}のように定義される
const fetchReducer = (state, action) => {
switch (action.type) {
// 通信成功時のアクションの場合
case "FETCH_SUCCESS":
return {
isError: false,
// stateのdataに通信で取得した値が格納される
data: action.payload,
};
// 通信失敗時のアクションの場合
case "FETCH_ERROR":
return {
isError: true,
data: state,
};
default:
return state;
}
};
function App() {
// useReducerにfetchReducerとinitialStateを登録
// dataにReducerで扱っている状態が入り、dispatchにActionを与えることを通じてStateを更新
const [data, dispatch] = useReducer(fetchReducer, initialState);
// 入力中の値をuseStateを使って定義
const [inputId, setInputId] = useState("");
// APIで送信する値をuseStateを使って定義
const [id, setId] = useState("");
// useEffectでは副作用を伴う処理の関数を引数に与える
// DOMのRenderが終了後に実行される
// 第二引数に空配列を与えると初回Render後一度だけ実行される
// 第二引数の配列に監視したい値を入れるとその値が変化した時だけ実行されるようになる
useEffect(() => {
// 初回Render時のidに何も値がない場合は実行しない
if (id === "") return;
// アンマウントされた時に仕様
let unmounted = false;
// axiosで非同期通信
axios
.get(`https://jsonplaceholder.typicode.com/todos/${id}`)
.then((data) => {
// アンマウントがされているなら状態を変更しない
if (unmouted) return;
// dispatchに通信成功のアクションを発行。payloadには通信で取得した値を入れる
dispatch({ type: "FETCH_SUCCESS", payload: data });
})
.catch((error) => {
console.log(error);
// 通信失敗時のアクションを発行
dispatch({ type: "FETCH_ERROR" });
});
// 入力途中の値を空にする
setInputId("");
// useEffectではundefinedか関数を返すことができる
// 関数を返した場合クリーンアップ関数として登録される
// クリーンアップ関数とはアンマウントされた時などに実行される
// 今回は先程定義したunmoutedにtrueを代入する
// アンマウントされた場合unmoutedにtrueが代入され、
// 状態を変更してもRenderする画面無くなっているのでif(unmouted)return;で状態を変更しない
return () => (unmounted = true);
// 第二引数の配列にidを指定する。idが変更された場合useEffect関数が実行される。
}, [id]);
return (
<div className="App">
<form>
<input
type="text"
value={inputId}
// inputIdをバインディングし入力された値を監視
onChange={(e) => setInputId(e.target.value)}
/>
<input
type="button"
value="取得"
onClick={() => {
// クリックされたらidに入力中の値をセットする。
// ここでidの値が変化するのでuseEffectが実行される
setId(inputId);
}}
/>
</form>
{/* useEffect内の通信で取得したデータがdispatchされReducerが状態を変化後Renderされる */}
<div>{JSON.stringify(data.data.data)}</div>
</div>
);
}
export default App;
おわりに
Reduxなどを使わずに状態を手軽に管理できるのはいいと思った。