LoginSignup
206
127

More than 5 years have passed since last update.

React Hooksの概要

Last updated at Posted at 2018-10-29

Hooksは、classを記述せずにstateやその他のReactの機能を使えるようにしたもので、今は提案段階です。RFCはこちら

現在はReact v16.7.0-alphaで試せます。

Hooks導入の動機はこちら
https://qiita.com/tatane616/items/9d667e32b6f82f9f7a9f

State Hook

よくあるボタンを押すとカウントが増えていくサンプルです。

import { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

この中のuseStateがHookです。
関数コンポーネントの中でもローカルステートを利用できるようにしたもので、現在のstateとstateを更新する関数の2つを返します。
古いstateと新しいstateを一緒にマージしないと言う点をのぞいて、classのthis.stateに似ています。

useStateの引数の0は初期値で、this.stateとは違いオブジェクトである必要はありません。

State Hookは1つのコンポーネントで複数利用でき、その場合は次のように記述します。

function ExampleWithManyStates() {
  // Declare multiple state variables!
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
  // ...
}

Hooksは、stateやライフサイクルメソッドを関数コンポーネントから利用する機能で、class内では機能しません。

Effect Hook

useEffectは関数コンポーネントで副作用を実装できるようにしたHooksで、データをフェッチしてきたり、DOMをいじったりなど今までclass内のcomponentDidMount,componentDidUpdate,componentWillUnmountでやってきたロジックを単一のAPIに統合すると言うもの。

DOMのアップデート後にtitleを変えるサンプル

import { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

Effectsはコンポーネント内で宣言され、propsやstateにアクセスが可能。デフォルトでは、初回を含むレンダリングのたびに実行されます。

Effectsでは、任意で関数を返すことによってどのようにクリーンアップするかを指定することもできます。
次のコードは、友人のオンラインステータスをsubscribeし、のちにunsubscribeするサンプルです。

import { useState, useEffect } from 'react';

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);

    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

この例では、コンポーネントがアンマウントされる時や再レンダリングでEffectが実行される時にChatAPIをunsubscribeします。

useStateと同様に、Effectも1つのコンポーネントに複数宣言することができます。

function FriendStatusWithCounter(props) {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }
  // ...

Hooksのルール

HooksはJSの関数ですが、2つのルールがあります。

  • トップレベルでのみ呼び出す。ループやifなどの条件文、ネストされた関数の中で呼び出してはいけない。
  • Reactの関数コンポーネントでのみ呼び出す。普通のJSの関数で呼び出してはいけない。

Custom Hooks

ステートを持ったロジックをコンポーネント間で再利用したい時は、今まではHOCやrender propsで解決していたと思いますが、Custom Hooksを使うことでツリーに余計なコンポーネントを追加せずに実装することができます。

先ほどのオンラインステータスのコードを例に見ていきましょう。
これらのロジックをuseFriendStatusと言うHookに抽出します。

import { useState, useEffect } from 'react';

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}

friendIDを引数にとって、友人がオンライン状態かどうかを返します。

このCustom Hooksは以下のように利用することができます。

function FriendStatus(props) {
  const isOnline = useFriendStatus(props.friend.id);

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}
function FriendListItem(props) {
  const isOnline = useFriendStatus(props.friend.id);

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

上記のコンポーネントのstateはそれぞれ独立しています。Hooksはステートフルなロジックを再利用するもので、それ自体がstateを持つわけではありません。

関数の名前をuseから始めることで、それを他のコンポーネントから呼んだ際にCustom Hookと言うことができます。
useSomethingと言う名前は、リンタープラグインなどでHooksを利用したコードでのバグを見つけることなどに役立ちます。

Other Hooks

他にもuseContextuseReducerといった便利なHooksがあります。

useContextは、ネストすることなくReact Contextを利用することができます。

function Example() {
  const locale = useContext(LocaleContext);
  const theme = useContext(ThemeContext);
  // ...
}

useReducerは複雑なコンポーネントでのローカルステートをreducerを使って管理できるようになります。

function Todos() {
  const [todos, dispatch] = useReducer(todosReducer);
  // ...

Hooks API reference

206
127
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
206
127