【React】フックとカスタムフック

前回はステートを使用して状態(データ)を管理しました。

ステートはReactが提供する機能です。
このような機能はフックと呼ばれ、ステート以外にも存在します。Reactが提供するフックをビルトインフックと言います。また、フックは自身で作成する事も可能で、自身で作成したフックをカスタムフックと言います。

フックとは

フックは再利用可能な機能の事です。例えば今まで使用してきたステートもインポートするだけ簡単に利用する事ができました。

カスタムフックとは

実態

カスタムフックは自身で作成したフックの事です。カスタムフックと言っても実態はフックを使って実装された関数の事です。

再利用と共通化

フックは再利用を目的としています。
コーディングしている中で同じような処理を書く場面がある場合、関数化(共通化)しようと思うでしょう。同じ処理を関数化して再利用するようなイメージです。
コーディングや例を見ながらイメージを掴んでいきましょう。

カスタムフックの例

カスタムフックに触れる前にカスタムフックを使用せず、「初めにやりたい事」と「次にやりたい事」をやってみようと思います。

初めにやりたい事

コーヒーの数量を設定し画面に表示させます。ルールは以下の通りです。

・「プラス」ボタンを押すと、コーヒーの数量が1増える。最大は3。
・「マイナス」ボタンを押すと、コーヒーの数量が1減る。最小は1。
・「リセット」ボタンを押すと、コーヒーの数量が1になる。

以下のようなアウトプットになります。

コンポーネント

今回は、以下の2つのコンポーネントを使用します。

・Appコンポーネント
ステートにてコーヒー数量を保持するコンポーネント。

・Orderコンポーネント
コーヒーの数量を選択できるコンポーネント。

Appコンポーネント

Appコンポーネントはステートを利用してコーヒーの数量を保持します。 Orderコンポーネントへステート(コーヒーの数量とコーヒーの数量を更新する関数)をプロパティ経由で渡しています。ここら辺は特別新しいことはしていません。

import React from 'react';
import Order from './Order';

function App() {

  // 選択されたコーヒーの数を保持するステート
  const [orderVal, setOrderVal] = React.useState(1);
  
  // 子コンポーネントを呼び出す
  return (
    <>
      <Order val={orderVal} setVal={setOrderVal} />
    </>
  );
}

export default App;

Orderコンポーネント

プラスボタンを押すとincrementOrder関数が呼ばれます。この関数はコーヒーの数を1増やします。コーヒーの数量が3以上の場合、コーヒーの数を3へ設定しています。

マイナスボタンを押すとdecrementOrder関数が呼ばれます。この関数はコーヒーの数を1減らします。コーヒーの数量が1以下の場合、コーヒーの数を1へ設定しています。

リセットボタンを押すとresetOrder関数が呼ばれます。この関数はコーヒーの数を1にします。

function Order({val, setVal}) {

    // コーヒーのオーダー数を1増やす
    const incrementOrder = () => {

        // コーヒーのオーダー数が3以上の場合、コーヒーのオーダー数を3にする。
        if(val >= 3){
            setVal(3);
            return;
        }

        setVal((val) => val + 1);
    };

    // コーヒーのオーダー数を1減らす
    const decrementOrder = () => {

        // コーヒーのオーダー数が1以下の場合、コーヒーのオーダー数を1にする。
        if(val <= 1){
            setVal(1);
            return;
        }

        setVal((val) => val - 1);
    };

    // コーヒーのオーダー数を1にする
    const resetOrder = () => setVal(1);

    // ブラウザ表示処理
    return (
      <>
        <p>注文するコーヒーの数を指定ください。</p>
        <div>
            <button onClick={incrementOrder}>プラス</button>
            <button onClick={decrementOrder}>マイナス</button>
            <button onClick={resetOrder}>リセット</button>
        </div>
        <p>コーヒーの数:{val}</p>
      </>
    );
  }
  
  export default Order;

次にやりたい事

コーヒー以外に、ミルクも注文できようにします。コーヒーの注文数と同じルールとします。

・「プラス」ボタンを押すと、ミルクの数量が1増える。最大は3。
・「マイナス」ボタンを押すと、ミルクの数量が1減る。最小は1。
・「リセット」ボタンを押すと、ミルクの数量が1になる。

以下のようなアウトプットになります。

コンポーネント

今回はAppコンポーネント、OrderコンポーネントとMilkOrderコンポーネントを使用します。

・Appコンポーネント
コーヒーの数量とミルクの数量をステートで保持するコンポーネント。

・Orderコンポーネント
変更はありません。ミルクの数量を選択できるコンポーネント。

・MilkOrderコンポーネント
ミルクの数量を選択できるコンポーネント。

Appコンポーネント

コーヒーの数量だけでなく、ミルクの数量もステートで管理します。また、MilkOrderコンポーネントへステートを渡しています。

import React from 'react';
import Order from './Order';
import MilkOrder from './MilkOrder';

function App() {

  // 選択されたコーヒーの数を保持するステート
  const [orderVal, setOrderVal] = React.useState(1);

  // 選択されたミルクの数を保持するステート
  const [milkOrderVal, setMilkOrderVal] = React.useState(1);
  
  // 子コンポーネントを呼び出す
  return (
    <>
      <Order val={orderVal} setVal={setOrderVal} />
      <MilkOrder val={milkOrderVal} setVal={setMilkOrderVal} />
    </>
  );
}

export default App;

Orderコンポーネント

変更はないので割愛します。

MilkOrderコンポーネント

プラスボタンを押すとincrementOrder関数が呼ばれます。この関数はミルクの数を1増やします。ミルクの数量が3以上の場合、ミルクの数を3へ設定しています。

マイナスボタンを押すとdecrementOrder関数が呼ばれます。この関数はミルクの数を1減らします。ミルクの数量が1以下の場合、ミルクの数を1へ設定しています。

リセットボタンを押すとresetOrder関数が呼ばれます。この関数はミルクの数を1にします。

function MilkOrder({val, setVal}) {

    // ミルクのオーダー数を1増やす
    const incrementOrder = () => {

        // ミルクのオーダー数が3以上の場合、ミルクのオーダー数を3にする。
        if(val >= 3){
            setVal(3);
            return;
        }

        setVal((val) => val + 1);
    };

    // ミルクのオーダー数を1減らす
    const decrementOrder = () => {

        // ミルクのオーダー数が1以下の場合、ミルクのオーダー数を1にする。
        if(val <= 1){
            setVal(1);
            return;
        }

        setVal((val) => val - 1);
    };

    // ミルクのオーダー数を1にする
    const resetOrder = () => setVal(1);

    // ブラウザ表示処理
    return (
      <>
        <p>注文するミルクの数を指定ください。</p>
        <div>
            <button onClick={incrementOrder}>プラス</button>
            <button onClick={decrementOrder}>マイナス</button>
            <button onClick={resetOrder}>リセット</button>
        </div>
        <p>ミルクの数:{val}</p>
      </>
    );
  }
  
  export default MilkOrder;

カスタムフック

コーヒーの数の設定もミルクの数の設定も同じ事をやっている事に気づいた事でしょう。これを共通化しカスタムフックとして使用してみましょう。

共通化

hooks.js というファイルを作成します。名前はなんでもいいです。このファイルにフックを集約するという意味で hooks という名称にしました。

useOrderCountという関数を作成しました。注文の数量をステートで保持しています。
先程は、コーヒーもミルクも注文数が同じように選べました。これを共通化する為、注文を1増やす関数(incrementOrder)、注文を1減らす関数(decrementOrder)、注文をリセットする関数(resetOrder)を作成しました。
返却値は配列で、第一引数は注文数、第二引数は、注文を1増やす関数、注文を1減らす関数、注文をリセットする関数をオブジェクトで返却しています。
なお、関数名はフックを使うということからプレフィックスに “use” をつけて “use〜” という名称にするのが一般的のようです。

import React from 'react';

export const useOrderCount = (val=1) => {

    // 注文の数量を管理するステート
    const [orderVal, setOrderVal] = React.useState(val);

    // 注文のオーダー数を1増やす
    const incrementOrder = () => {

        // 注文のオーダー数が3以上の場合、注文のオーダー数を3にする。
        if(orderVal >= 3){
            setOrderVal(3);
            return;
        }

        setOrderVal((orderVal) => orderVal + 1);
    };

    // 注文のオーダー数を1減らす
    const decrementOrder = () => {

        // 注文のオーダー数が1以下の場合、注文のオーダー数を1にする。
        if(orderVal <= 1){
            setOrderVal(1);
            return;
        }

        setOrderVal((orderVal) => orderVal - 1);
    };

    // 注文のオーダー数を1にする
    const resetOrder = () => setOrderVal(1);

    // 第一引数にはデータ、第二引数にはデータを更新する関数として配列を返却する。
    return [orderVal, {incrementOrder, decrementOrder, resetOrder}];
};

このようにフック(今回はステート)を利用した関数を再利用する事をカスタムフックと言います。

カスタムフックの利用

カスタムフックの利用は簡単で、先程の hooks からエクスポートされたカスタムフック(useOrderCount) をインポートして使用するだけです。

以下はAppコンポーネントになります。カスタムフックから返却される値を、各コンポーネントへ渡しています。

import React from 'react';
import { useOrderCount } from "./hooks";
import Order from './Order';
import MilkOrder from './MilkOrder';

function App() {

  // 選択されたコーヒーの数を保持するステート
  const [cofferOrderVal, funcA] = useOrderCount(1);

  // 選択されたミルクの数を保持するステート
  const [milkOrderVal, funcB] = useOrderCount(1);
  
  // 子コンポーネントを呼び出す
  return (
    <>
      <Order val={cofferOrderVal} setVal={funcA} />
      <MilkOrder val={milkOrderVal} setVal={funcB} />
    </>
  );
}

export default App;

注文数の表示

最後にコーヒーの注文数を表示するOrderコンポーネントと、ミルクの注文数を表示するMilkOrderコンポーネントになります。

以下は、Orderコンポーネントです。

function Order({val, setVal}) {

    // ブラウザ表示処理
    return (
      <>
        <p>注文するコーヒーの数を指定ください。</p>
        <div>
            <button onClick={setVal.incrementOrder}>プラス</button>
            <button onClick={setVal.decrementOrder}>マイナス</button>
            <button onClick={setVal.resetOrder}>リセット</button>
        </div>
        <p>コーヒーの数:{val}</p>
      </>
    );
  }
  
  export default Order;

以下は、MilkOrderコンポーネントです。

function MilkOrder({val, setVal}) {

    // ブラウザ表示処理
    return (
      <>
        <p>注文するミルクの数を指定ください。</p>
        <div>
            <button onClick={setVal.incrementOrder}>プラス</button>
            <button onClick={setVal.decrementOrder}>マイナス</button>
            <button onClick={setVal.resetOrder}>リセット</button>
        </div>
        <p>ミルクの数:{val}</p>
      </>
    );
  }
  
  export default MilkOrder;

Orderコンポーネント、MilkOrderコンポーネント ともに数量を変更する関数が引数で渡される為、記載がなくなりスッキリしました。

最後に

特にありません。

コメントを残す

メールアドレスが公開されることはありません。

© DeNnie.Lab All Rights Reserved.