React Hooksとは?

Posted: 2020/9/7
React/
React Hooks

React Hooks

React Hook を用いると state などの React の機能をクラスを書かずに関数で使うことができます。

useState

useState を呼び出すことで、関数で state を扱うことができます。useStateの引数には state の初期値を渡します。また useState は現在の stateとそれを更新するための関数を返します。

import React, { useState } from "react";

const MessageBox = () => {
  const [message, setMessage] = useState("default");

  return (
    <div>
      <p>Your message: {message}</p>
      <input type="text" onChange={e => setMessage(e.target.value)} />
    </div>
  );
};

useEffect

関数コンポーネント内で副作用(データの取得、subscription の設定、DOM の変更など)を行うことを実行することができます。

ライフサイクルの観点では、 useEffectcomponentDidMountcomponentDidUpdatecomponentWillUnmountがまとまったものだと考えてよいです。

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

const MessageBox = () => {
  const [message, setMessage] = useState("default");

  useEffect(() => {
    console.log(`Your message: ${message}`);
  });

  return (
    <div>
      <p>Your message: {message}</p>
      <input type="text" onChange={e => setMessage(e.target.value)} />
    </div>
  );
};

上記の例の場合、 useEffect は初回を含むレンダー毎に呼び出されます(クラスコンポーネントの場合、componentDidMount と componentDidUpdate 両方に相当)

クリーンアップ

useEffect 内でクリーンナップするための関数を返すことができます。クリーンアップのタイミングはコンポーネントがアンマウントされる時(クラスコンポーネントでのcomponentWillUnmount)です。

useEffect(() => {
  console.log(`Your message: ${message}`);
  return function cleanUp() {
    // クリーンアップする処理
  };
});
  • useEffect は複数呼び出すことができ、ロジックを分けることもできます。

条件付きで useEffect を実行

useEffect の第二引数に依存している値の配列を渡します(複数渡すことも可)。以下の例だと、message が変更された時のみuseEffectが実行されます。

useEffect(() => {
  console.log(`Your message: ${message}`);
  return function cleanUp() {
    // クリーンアップする処理
  };
}, [message]);
  • 第二引数に空配列[]を渡すと、どの値にも依存していないということになり、更新時にuseEffectが実行されなくなる

useContext

import React, { useContext } from "react";

const SampleContext = React.createContext("hoge");

function HookContext() {
  return (
    <SampleContext.Provider value="fuga">
      <Bar />
    </SampleContext.Provider>
  );
}

function Bar() {
  return (
    <div>
      <Button />
    </div>
  );
}

function Button() {
  // コンテキストオブジェクトの値を取得する
  const text = useContext(SampleContext);
  return <>Context Value: {text}</>;
}

useContext は最も近いコンテキストオブジェクトの値(<MyContext.Provider>value)を返し、引数にはコンテキストオブジェクトそのものを渡します。

useReducer

const [state, dispatch] = useReducer(reducer, initialArg);

useStateの代わりとなり、引数に

  • reducer : 現在の state とアクションを受け取り、新しい state を返す関数
  • initialState : state の初期値

を取り、現在のstatedispatchメソッド(アクションを発火する関数)を返します。

useStateに代わって使用するのは、state を前の state に基づいて更新する場合、state に依存するロジックを用いる場合などです。

import React, { useReducer } from "react";

const initialState = { count: 0 };

// reducerにロジックを詰め込む
function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { count: state.count + 1 };
    case "decrement":
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

function Counter() {
  // reducerと初期値を引数に代入し、stateとdispatchを返す
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: "decrement" })}>-</button>
      <button onClick={() => dispatch({ type: "increment" })}>+</button>
    </>
  );
}

useMemo

const CalculateMemo = useMemo(() => computeValue(x, y, z), [x, y, z]);

useMemo は関数とそれに依存する値の配列を引数にもち、メモ化した値を返します。第二引数の配列の要素が変化した場合のみメモ化した値を再計算します。

メモ化とは、計算結果を保持して、それを再利用する手法であり、パフォーマンスの向上が期待できます。

useCallback

const CalculateCallback = useCallback(() => computeValue(x, y, z), [x, y, z]);

関数とそれに依存する値の配列を引数にもち、メモ化したコールバック関数を返します。

Hook のルール

以下、Hook のルールについて述べます。

  • フックを呼び出すのは関数のトップレベルのみ
    • ループや条件分岐・ネストされた関数内で呼び出してはいけない
    • React はフックが呼ばれる順番に依存しているため、条件分岐等で順番が変わると state の割り当てが変わる恐れがある
  • フックを呼び出すのは React の関数内のみ
  • eslint-plugin-react-hooks という ESLint のプラグインを導入することでこれらのルールを強制できる