【React】4ステップで作成するカスタムフック実装手順

JavaScript

前回、【React】カスタムフックの概要・メリット・使いどころでカスタムフックの概要について紹介しました。
今回はカスタムフックの具体的な作成手順について紹介します。

今回利用するサンプルコードについて

今回は『API経由で取得したTodoリストの内容を表示する』というコンポーネントを例にカスタムフックの作成手順について紹介します。

最終的には『Todoリストの内容を表示するコンポーネント』と『API経由でTodoリストを取得するカスタムフック』に分割します。

カスタムフック導入前

以下はカスタムフック導入前のコンポーネントです。
データ取得とUI構築のコードが混在しているため可読性があまりよくありません。

App.tsx

import axios from "axios";
import { useEffect, useState } from "react";

type TodoItem = {
  id: number;
  content: string;
  completed: boolean;
};

const App = () => {
  const [todos, setTodos] = useState([] as TodoItem[]);
  useEffect(() => {
    const getTodoRequest = async () => {
      const response = await axios.get("http://localhost:4000/todos");
      const todos = response.data;
      return todos;
    };
    getTodoRequest().then((todos) => setTodos(todos as TodoItem[]));
  }, []);
  return (
    <>
      <h2>TodoList</h2>
      <ul>
        {todos.map(({ id, content }) => (
          <li key={id}>
            <div>{content}</div>
          </li>
        ))}
      </ul>
    </>
  );
};
export default App;

カスタムフック導入後

以下は今回のゴールであるカスタムフック導入後のコードです。

src/lib/hooks/useTodos.ts

import axios from "axios";
import { useEffect, useState } from "react";

type TodoItem = {
  id: number;
  content: string;
  completed: boolean;
};

const useTodos = () => {
  const [todos, setTodos] = useState([] as TodoItem[]);
  useEffect(() => {
    const getTodoRequest = async () => {
      const response = await axios.get("http://localhost:4000/todos");
      const todos = response.data;
      return todos;
    };
    getTodoRequest().then((todos) => setTodos(todos as TodoItem[]));
  }, []);
  return todos;
};
export default useTodos;

App.tsx

import useTodos from "./lib/hooks/useTodos";

const App = () => {
  const todos = useTodos();
  return (
    <>
      <h2>TodoList</h2>
      <ul>
        {todos.map(({ id, content }) => (
          <li key={id}>
            <div>{content}</div>
          </li>
        ))}
      </ul>
    </>
  );
};
export default App;

今回はあくまでカスタムフックを理解するためのサンプルコードです。
API経由のデータ取得は例えばSWRを利用する方法などもあります。

カスタムフックの実装の手順について

カスタムフックの実装の手順をまとめると以下のようになります。

カスタムフックの実装手順
  1. コンポーネントのコードから分離させたいフックを検討する
  2. 関連するフックをカスタムフックへ移動する
  3. コンポーネントで利用するデータをカスタムフックの戻り値にする
  4. コンポーネントからカスタムフックを呼び出す

以下ではそれぞれの手順について説明します。

コンポーネントのコードから分離させたいフックを検討する

コンポーネントからカスタムフックへ実装を移動させるフックの検討をします。コンポーネントから分離したほうがよいフックは以下の通りです。

コンポーネントからの分離を検討したほうがよいフック
  • 処理が複雑なフック
  • 複数のコンポーネントで同じ実装がされているフック
  • データ取得に関する(Viewに関係しない)フック

関連するフックをカスタムフックへ移動する

分離したいフックの検討が終わったら、関連するフックをカスタムフックへ移動します。

今回は『Todoリストの取得』に関するフックをカスタムフックにしたいので、Todoリストを保存するuseStateとTodoリストを取得するuseEffectを移動します。

App.tsx

- import axios from "axios";
- import { useEffect, useState } from "react";

- type TodoItem = {
-   id: number;
-   content: string;
-   completed: boolean;
- };

const App = () => {

-  const [todos, setTodos] = useState([] as TodoItem[]);

-  useEffect(() => {
-    const getTodoRequest = async () => {
-      const response = await axios.get("http://localhost:4000/todos");
-      const todos = response.data;
-      return todos;
-    };
-    getTodoRequest().then((todos) => setTodos(todos as TodoItem[]));
-  }, []);
  return (
    <>
      <h2>TodoList</h2>
      <ul>
        {todos.map(({ id, content }) => (
          <li key={id}>
            <div>{content}</div>
          </li>
        ))}
      </ul>
    </>
  );
};
export default App;

src/lib/hooks/useTodos.ts

import axios from "axios";
import { useEffect, useState } from "react";

type TodoItem = {
  id: number;
  content: string;
  completed: boolean;
};

const useTodos = () => {
  const [todos, setTodos] = useState([] as TodoItem[]);
  useEffect(() => {
    const getTodoRequest = async () => {
      const response = await axios.get("http://localhost:4000/todos");
      const todos = response.data;
      return todos;
    };
    getTodoRequest().then((todos) => setTodos(todos as TodoItem[]));
  }, []);

};
export default useTodos;

コンポーネントで利用するデータをカスタムフックの戻り値にする

今回の場合、コンポーネントではTodoリストのデータを利用します。
ですので、カスタムフックの戻り値はTodoリストの保存されたオブジェクトにします。

src/lib/hooks/useTodos.ts

import axios from "axios";
import { useEffect, useState } from "react";

type TodoItem = {
  id: number;
  content: string;
  completed: boolean;
};

const useTodos = () => {
  const [todos, setTodos] = useState([] as TodoItem[]);
  useEffect(() => {
    const getTodoRequest = async () => {
      const response = await axios.get("http://localhost:4000/todos");
      const todos = response.data;
      return todos;
    };
    getTodoRequest().then((todos) => setTodos(todos as TodoItem[]));
  }, []);
+  return todos;
};
export default useTodos;

コンポーネントからカスタムフックを呼び出す

コンポーネントでカスタムフックをimportし、カスタムフックの戻り値を変数で受け取ります。
あとは受け取った変数を利用してUIを構築すればOKです。

+ import useTodos from "./lib/hooks/useTodos";

const App = () => {
+  const todos = useTodos();

  return (
    <>
      <h2>TodoList</h2>
      <ul>
        {todos.map(({ id, content }) => (
          <li key={id}>
            <div>{content}</div>
          </li>
        ))}
      </ul>
    </>
  );
};
export default App;

以上でカスタムフック導入後のコードが完成します。

さいごに

Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!