「Reactを勉強し始めたけど全体の仕組みがイマイチわかっていない」
「フックと言われても何のことか理解できずに学習を進めている」
「カスタムフックについてはスクールや学習教材では習わないから困っている」
本日はそんな方に向けてReactのカスタムフックについて解説していきます。
カスタムフック自体は必須の知識ではないのでゼロから教わることが少ないです。
とはいえカスタムフックの作り方を勉強することでReactの全体像までイメージしやすくなることが多いので余裕のある方は1回は練習しておきましょう。
また動画もあるので必要に応じて使ってください。
Reactにおけるカスタムフックの作り方
まず例題としてAPIからテキストデータを取得して画面に表示することをやってみたいと思います。
APIはjsonplaceholderを使用します。
https://jsonplaceholder.typicode.com/
import "./App.css";
import CustomHook from "./screens/CustomHook/CustomHook";
function App() {
return (
<div className="App">
<CustomHook />
</div>
);
}
export default App;
import React, { useEffect, useState } from "react";
const CustomHook = () => {
const [task, setTask] = useState("");
useEffect(() => {
const fetchData = async () => {
await fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((res) => res.json())
.then((data) => setTask(data.title));
};
fetchData();
}, []);
return (
<div>
{task}
</div>
);
};
export default CustomHook;
jsonplaceholderから使用したいURLをfetchメソッドで取得して定数taskに格納するだけの内容です。
正常に動作すると上図のように画面にテキストが表示されます。
この仕様は特に問題ないのですが、上記のコードから追加で色んな機能を追加していくとします。
そうした場合にCustomHook.jsの中で関数を定義するコードなどでボリュームが増えていくことが予想できます。
カスタムフックはそういったファイル内のコードが膨大になっていくことを想定して、関数の定義などコードの一部分を別のファイルに逃す考え方です。
上記のAPIから取得する部分のコードをカスタムフックとして別のファイルに逃してみましょう。
逃すための別のファイルを「useRandomTask.js」という名前で新規作成します。
CustomHook.jsで書いていたAPI取得の部分を丸っとコピペでuseRandomTask.jsに移動させましょう。
import React, { useEffect, useState } from "react";
const useRandomTask = () => {
const [task, setTask] = useState("");
useEffect(() => {
const fetchData = async () => {
await fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((res) => res.json())
.then((data) => setTask(data.title));
};
fetchData();
}, []);
return <div>useRandomTask</div>;
};
export default useRandomTask;
import React, { useEffect, useState } from "react";
const CustomHook = () => {
return (
<div>
{task}
</div>
);
};
export default CustomHook;
これでCustomHook.jsに書いたAPI取得のコードを別のファイルであるuseRandomTask.jsに逃しました。
ただこれだけでは正常に動作しません。
React自体の仕組みを思い出して欲しいのですが、基本的に「コンポーネント」という考え方で作るのでした。
そのため今回のようにファイルを分ける際には必ずインポートすることになります。
逃がしたコードをuseRandomTask.jsからCustomHook.jsにインポートしましょう。
import React, { useEffect, useState } from "react";
const useRandomTask = () => {
const [task, setTask] = useState("");
useEffect(() => {
const fetchData = async () => {
await fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((res) => res.json())
.then((data) => setTask(data.title));
};
fetchData();
}, []);
// ここを変更
return task;
};
export default useRandomTask;
まずuseRandomTask.jsですがreturnがHTMLタグになっていたのですが、useRandomTask.jsはあくまで画面に表示する役割ではないのでreturnの部分を変更しました。
HTMLタグではなく処理の結果が格納されている定数taskです。
あくまでAPIを取得してテキストデータを定数taskに格納してエクスポートするまでがuseRandomTask.jsの役目です。
続いて元々書いていたCustomHook.jsにてインポートします。
import React, { useEffect, useState } from "react";
// ここを追加
import useRandomTask from "./useRandomTask";
const CustomHook = () => {
// ここを追加
const task = useRandomTask();
return (
<div>
{task}
</div>
);
};
export default CustomHook;
インポート文の書き方は問題ないと思いますが、インポートしたものを反映するコードも必要になります。
今回は定数taskにテキストデータがあるはずなので、再度taskを用意してuseRandomTaskを関数として実行した戻り値を入れました。
画面の表示結果は変わりません、カスタムフックでコードを分割しただけで処理自体に変更は加えてないからです。
今回のような簡単なコードであればカスタムフックにする必要はないですが、初学者の方はこんな感じで簡単なコードで実験するところから練習してみてください。
オープンソースのコードを見ていると複雑なカスタムフックがたくさんありますが、そもそもの考え方はコードを別のファイルに逃しているだけです。
Reactにおける引数のあるカスタムフックの作り方
カスタムフックの説明は終わってしまったのですが、実務でよくある「引数」を使用するカスタムフックについても紹介して終わろうと思います。
例題は先ほどのコードを引き続き使用します。
import "./App.css";
import CustomHook from "./screens/CustomHook/CustomHook";
function App() {
return (
<div className="App">
<CustomHook />
</div>
);
}
export default App;
import React, { useEffect, useState } from "react";
import useRandomTask from "./useRandomTask";
const CustomHook = () => {
const task = useRandomTask();
return (
<div>
{task}
</div>
);
};
export default CustomHook;
import React, { useEffect, useState } from "react";
const useRandomTask = () => {
const [task, setTask] = useState("");
useEffect(() => {
const fetchData = async () => {
await fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((res) => res.json())
.then((data) => setTask(data.title));
};
fetchData();
}, []);
return task;
};
export default useRandomTask;
APIから取得するテキストデータは「id=1」のもので固定していたのですが、引数によってidの値を自由に選択できるように変えてみます。
まずは引数を自由に変えるための工夫として、画面上に入力欄を作って入力した数字をidとして使用することにします。
import React, { useEffect, useState } from "react";
import useRandomTask from "./useRandomTask";
const CustomHook = () => {
// ここを追加
const [inputText, setInputText] = useState("");
const task = useRandomTask();
return (
<div>
{/* ここを追加 */}
<form>
<input
type="text"
value={inputText}
onChange={(e) => setInputText(e.target.value)}
/>
</form>
{task}
</div>
);
};
export default CustomHook;
inputタグを使って入力欄を画面に表示しています。
また入力された数字を管理するためにuseStateで定数inputTextを用意しました。
定数inputTextにidになる数字が格納されるようになりました。
この定数inputTextを引数としてuseRandomTask.jsに渡します。
import React, { useEffect, useState } from "react";
import useRandomTask from "./useRandomTask";
const CustomHook = () => {
const [inputText, setInputText] = useState("");
// ここを変更
const task = useRandomTask(inputText);
return (
<div>
{/* ここを追加 */}
<form>
<input
type="text"
value={inputText}
onChange={(e) => setInputText(e.target.value)}
/>
</form>
{task}
</div>
);
};
export default CustomHook;
さらに渡された引数をuseRandomTask.jsの中で使用できるように少し変更を加えます。
まず引数をアロー関数にidという名前で設定します。
そしてfetchメソッド内のURLですが最後の部分を「1」で固定していましたが引数idに変えます。
これで引数idに入っている数字によって今までの「1」以外のテキストデータを取得することができます。
最後にuseEffectの第二引数に引数を追加します。
useEffectは引数がある場合には第二引数に追加しておくことで、画面上の入力欄で数字を入れ替えるたびにデータの取得を実行してくれる様になります。
import React, { useEffect, useState } from "react";
// ここを変更
const useRandomTask = (id) => {
const [task, setTask] = useState("");
useEffect(() => {
const fetchData = async () => {
// ここを変更
await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
.then((res) => res.json())
.then((data) => setTask(data.title));
};
fetchData();
// ここを変更
}, [id]);
return task;
};
export default useRandomTask;
画面上の入力欄に入れた数字によって表示されるテキストデータが変わりました。
定数inputTextには入力された数字が入っていて、それが引数としてuseRandomTask.jsに渡されてAPIのURLが書き換えられる仕組みです。
inputTextの部分は数字をそのまま入力してもデータが取得できます。
import React, { useEffect, useState } from "react";
import useRandomTask from "./useRandomTask";
const CustomHook = () => {
const [inputText, setInputText] = useState("");
// ここを変更(数字をそのまま入力してもデータが取れる)
const task = useRandomTask(3);
return (
<div>
{/* ここを追加 */}
<form>
<input
type="text"
value={inputText}
onChange={(e) => setInputText(e.target.value)}
/>
</form>
{task}
</div>
);
};
export default CustomHook;
入力欄に何も入力していませんがテキストデータが表示できていますね。
このことから定数inputTextに数字が格納されていて、引数によってuseRandomTask.jsが動作していることがわかります。
冒頭でも説明しましたがカスタムフックは必須の知識ではないです。
あくまで開発している内容やチーム体制によって必要になるだけなので、初学者向けの学習教材やスクールでは詳しく解説されません。
しかし実務で経験を積んでいくと必要になるタイミングが来るのと、WordPressでもカスタムフックの考え方は登場します。
WordPressはPHPなので厳密には同じ意味ではないですが、考え方は同じですので裏側で何をしているかは知っておいて損ないでしょう。