NRIネットコム Blog

NRIネットコム社員が様々な視点で、日々の気づきやナレッジを発信するメディアです

【React】カスタムフックを実装してみる

本記事は  Reactウィーク  2日目の記事です。
📍  1日目  ▶▶ 本記事 ▶▶  3日目  📱

はじめに

こんにちは、NRIネットコムの中野です。
先日食べたローソンの「ザクザクショコラチーズケーキ」のおいしさが頭から離れない今日この頃です。
今回はReactウィークということで、カスタムフックを使ってコンポーネントをリファクタリングする流れを書いてみたいと思います。
Reactアプリを開発するようになって2年が経とうとしていますが、最初のころはカスタムフックって何?難しそう・・と敬遠していた記憶があります。 実際にやってみると案外簡単に作れますし、使い勝手も良いので、まだカスタムフック作ったことないよ~という方の取っ掛かりになれば幸いです。

カスタムフックとは

カスタムフックは、Reactで使われる再利用可能なロジックのことです。
useStateやuseEffectといったフックを組み合わせて独自のロジックを作成することができ、コンポーネント間での再利用が可能になります。

カスタムフックを使うメリット

カスタムフックを使うメリットは大きく2点あります。

  1. ロジックを再利用できる

  2. コードの可読性が上がる

複数のコンポーネントで使用される可能性のある状態管理やロジックをカスタムフックとして作ることで、重複するコードが生まれることを避けることができます。
また、再利用性の高いカスタムフックを作成することでアプリケーション全体の可読性や柔軟性が向上し、開発効率もアップします。

実際に作ってみる

ではさっそく、カスタムフックを作ってみましょう。
まずは簡単なメニューコンポーネントを作成します。今回はMUIを使って実装しています。 mui.com

メニューコンポーネント

SampleMenu.tsx

import { useState } from "react";
import { Button, Menu, MenuItem } from "@mui/material";

export const SampleMenu = () => {
  // メニューを表示するためのアンカーポイント(ボタン要素)を管理
  const [menuAnchorEl, setMenuAnchorEl] = useState<null | HTMLElement>(null);
  const handleButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    setMenuAnchorEl(event.currentTarget);
  };
  const handleMenuClose = () => {
    setMenuAnchorEl(null);
  };
  return (
    <div>
      <Button variant="contained" onClick={handleButtonClick}>
        Open Menu
      </Button>
      <Menu
        open={Boolean(menuAnchorEl)}
        onClose={handleMenuClose}
        anchorEl={menuAnchorEl}
      >
        <MenuItem onClick={handleMenuClose}>メニュー1</MenuItem>
        <MenuItem onClick={handleMenuClose}>メニュー2</MenuItem>
        <MenuItem onClick={handleMenuClose}>メニュー3</MenuItem>
      </Menu>
    </div>
  );
};
  • ボタンをクリックするとhandleButtonClick関数が呼ばれ、メニューが表示されます

  • メニューアイテムをクリックするとhandleMenuClose関数が呼ばれ、メニューが閉じます

この状態からメニュー開閉をカスタムフックとして分離し、他のコンポーネントからも利用できるようリファクタリングしてみます。
カスタムフックは"use"から始まる名前にするというルールがあるので、useMenuという名前で作ります。

useMenu.ts

import { useState } from 'react';

export const useMenu = () => {
  const [menuAnchorEl, setMenuAnchorEl] = useState<null | HTMLElement>(null);
  const handleButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    setMenuAnchorEl(event.currentTarget);
  };
  const handleMenuClose = () => {
    setMenuAnchorEl(null);
  };

  return {
    menuAnchorEl,
    handleButtonClick,
    handleMenuClose,
  };
};

SampleMenu.tsx

import { Button, Menu, MenuItem } from '@mui/material';
import { useMenu } from './useMenu';

export const SampleMenu = () => {
  const { handleButtonClick, menuAnchorEl, handleMenuClose } = useMenu();
  return (
    <div>
      <Button variant='contained' onClick={handleButtonClick}>
        Open Menu
      </Button>
      <Menu open={Boolean(menuAnchorEl)} onClose={handleMenuClose} anchorEl={menuAnchorEl}>
        <MenuItem onClick={handleMenuClose}>メニュー1</MenuItem>
        <MenuItem onClick={handleMenuClose}>メニュー2</MenuItem>
        <MenuItem onClick={handleMenuClose}>メニュー3</MenuItem>
      </Menu>
    </div>
  );
};

useMenuにメニュー開閉のロジックをまるっと移行し、SampleMenuからuseMenuを呼び出すようになりました。
状態管理の部分をカスタムフックに分離したことでより簡潔で再利用性の高いコードにすることができました。

まとめ

今回はMUIを使ってシンプルなメニューを作り、さらにメニューの開閉をカスタムフックとして分離するという一連の流れを書いてみました。
カスタムフックは共通性のあるロジックを複数のコンポーネントから利用できるようにするだけでなく、 ロジックが複雑になってきた際にカスタムフックとして切り出すことで、コードの視認性もよくなり、コンポーネントをシンプルに保つことができます。
状態管理やロジックを整理したい場合にぜひ活用してみてください。

執筆者:中野杏香 Webアプリケーションエンジニア