👍
3ステップで実現するカスタムフック作成ガイド
カスタムHooksの使い方を早く知りたい方へ送る、初心者向け記事です。
keyword:React, customHooks, カスタムフック, 初心者
0. カスタムHooksはどんなときに使う?
以下のとおり、主にHooks周りでごちゃごちゃしているところを、分離したいときに使います。
※詳しくは、ReactのカスタムHooksをカジュアルに使ってコードの見通しを良くしようという良記事を参照願います
- before:カスタムHookがないとき
- after:カスタムHookがあるとき
1. 分離したいところを別コンポーネントにコピペする
さっそくカスタムHook化していきましょう。
上記サンプルのうち、Hooks周りでごちゃごちゃしているところを、新しく作ったファイルにコピペします。
このままだとエラーが出たりしますが、ひとまず気にしなくて大丈夫です。
カスタムHooksを利用する前のサンプルコード
App.js
import { useState } from "react";
import { API_URL } from "../src/consts/urls";
import { useToastMessage } from "./useToastMessage";
export const App = () => {
const [keyword, setKeyword] = useState("");
const [books, setBooks] = useState();
const { showMessage } = useToastMessage();
const searchBooks = async (keyword) => {
try {
//APIからデータをfetchする
const r = await fetch(`${API_URL}?q=intitle:${keyword}&maxResults=15`);
const data = await r.json();
if (!r.ok) {
throw new Error("エラーが発生しました");
}
if (data.totalItems === 0) {
setBooks(null);
} else {
const filteredData = data.items.map((item, i) => {
return {
id: item.id,
number: i,
title: item.volumeInfo.title,
thumbnail: item.volumeInfo.imageLinks
? item.volumeInfo.imageLinks.smallThumbnail
: undefined,
};
});
setBooks(filteredData);
}
//通信失敗時はエラーをtoastで表示する
} catch (e) {
console.error(e);
return showMessage({ title: "エラーが発生しました", status: "error" });
}
};
return (
<>
<input
pr="4.5rm"
id="input"
bg="gray.100"
name="input"
placeholder="本のタイトルを入力"
type="text"
value={keyword}
onChange={(e) => {
setKeyword(e.target.value);
}}
/>
<button type="submit" onClick={() => {
searchBooks(keyword);
}} />// input
{books && books[0].title} //booksがあるときに1つめのタイトルを表示
</>
);
};
useSearchBooks.jsx
import { useState } from "react";
import { API_URL } from "../src/consts/urls";
import { useToastMessage } from "./useToastMessage";
export const App = () => {
const [keyword, setKeyword] = useState("");
const [books, setBooks] = useState();
const { showMessage } = useToastMessage();
const searchBooks = async (keyword) => {
try {
//APIからデータをfetchする
const r = await fetch(`${API_URL}?q=intitle:${keyword}&maxResults=15`);
const data = await r.json();
if (!r.ok) {
throw new Error("エラーが発生しました");
}
if (data.totalItems === 0) {
setBooks(null);
} else {
const filteredData = data.items.map((item, i) => {
return {
id: item.id,
number: i,
title: item.volumeInfo.title,
thumbnail: item.volumeInfo.imageLinks
? item.volumeInfo.imageLinks.smallThumbnail
: undefined,
};
});
setBooks(filteredData);
}
//通信失敗時はエラーをtoastで表示する
} catch (e) {
console.error(e);
return showMessage({ title: "エラーが発生しました", status: "error" });
}
};
2. 上下にちょっと付け足す
カスタム Hookの名前と関数名をつけて、使いたい変数やステートをreturnさせる。
useSearchBooks.jsx
import { useCallback, useState } from "react";
import { API_URL } from "../src/consts/urls";
import { useToastMessage } from "./useToastMessage";
export const useSearchBooks = () => { //1. useなんちゃらという名前をつけてnamed export
const [keyword, setKeyword] = useState("");
const [books, setBooks] = useState();
const { showMessage } = useToastMessage();
const searchBooks = useCallback( //2. 関数名をつけてメモ化する
async (keyword) => {
try {
const r = await fetch(`${API_URL}?q=intitle:${keyword}&maxResults=15`);
const data = await r.json();
if (!r.ok) {
throw new Error("エラーが発生しました");}
if (data.totalItems === 0) {
setBooks(null);
} else {
const filteredData = data.items.map((item, i) => {
return {
id: item.id,
number: i,
title: item.volumeInfo.title,
thumbnail: item.volumeInfo.imageLinks
? item.volumeInfo.imageLinks.smallThumbnail
: undefined};
});
setBooks(filteredData);
}
} catch (e) {
console.error(e);
return showMessage({ title: "エラーが発生しました", status: "error" });
}
},
[setBooks, showMessage]
);
return { searchBooks, books, keyword, setKeyword }; //3. 使いたいものをreturnする
};
3. 使いたいところに1行足す
分離元のコンポーネントに戻りカスタムHookをインポートする。
カスタムHook化した箇所を削除し、1行足すと無事完成です。
App.js
import { useSearchBooks } from "../../../Hooks/useSearchBooks"; //カスタムHookをインポート
export const App = () => {
const { searchBooks, books, keyword, setKeyword } = useSearchBooks(); //カスタムHookでreturnしたものを呼び出す。カスタムHook名に()をつけるのを忘れないように。
return (
<>
<input
placeholder="本のタイトルを入力"
type="text"
value={keyword}
onChange={(e) => {
setKeyword(e.target.value);
}}
/>
<button
type="submit"
onClick={() => {
searchBooks(keyword);
}}
/>
// input
{books && books[0].title}
</>
);
};
<完>
サンプルコード集
記事の元ネタのコードや別のチュートリアルコードを公開しています
Discussion