React Hooks がやってくる

ここで紹介する機能は アルファ版です(2019年1月現在)。本番環境で運用するプロジェクトにはまだ利用しないでください。

React Hooks

React の新機能 Hooks が話題になっています。

Introducing Hooks – React

Hooks のメリット

機能追加の背景については Motivation に詳しく書かれていますが、 これまで React には下記のような課題がありました。

  • ステートレスなコンポーネント(= 関数型コンポーネント)をステートフルに改修する際、クラス型のコンポーネントにリファクタする必要があった
    • リファクタによるコストが大きい
  • コンポーネント間でステートフルな情報を共有する(例えば store にアクセスする)際、High-Order Component や Provider・Consumer の手法を使う 必要があった
    • いわゆる “ラッパー地獄” を招いた

Hooks は下記のような特徴を備えることで、上記の課題を解決します。

  • 既存のコンポーネント階層を変えることなく、ステートフルなロジックを取り入れる
  • あるコンポーネントのステートフルなロジックを 別のコンポーネントに流用できる

基本の使い方

例えば、this.props.count だけを表示するようなコンポーネント CountViewer があったとします。

function CountViwer({count}) {
  return (
    <>Count: {count}</>
  );
}

これまで、このコンポーネントにステートをもたせた Counter コンポーネントへ改修するには、クラスコンポーネントに リファクタする必要がありました。

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: props.initialCount };
  }
  return (
    <>
      Count: {count}
      <button onClick={() => this.setState({ count: 0 })}>Reset</button>
      <button onClick={() => this.setState({ count } => ({ count: count + 1})}>+</button>
      <button onClick={() => this.setState({ count } => ({ count: count - 1})}>-</button>
    </>
  );
}

もはや別物ですね。。

useState を使うと、こうなります。

import { useState } from 'react';

function Counter({initialCount}) {
  const [count, setCount] = useState(initialCount);
  return (
    <>
      Count: {count}
      <button onClick={() => setCount(0)}>Reset</button>
      <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
      <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
    </>
  );
}

関数型コンポーネントに、ステートを注入しています。

概説

useState[state, setState] を返却します。これは、今まで(クラスコンポーネント)の statesetState を返却することに相当しますが、 この setState はペアで返却された state のみを更新できます。

また、useState に引数を与えると、state の初期値となります。 上記の例の場合、(props として受け取った) initialCount が初期の count になります。

初期値を求めるために処理が必要な場合、useState の引数を関数にすることもできます。 この場合、初期値を関数の返却値にしてください。

Lazy initialization より引用)

useEffect の利用

関数型コンポーネント内で副作用のある処理を直接書くと、バグが発生しやすく、またデータと UI の不一致などを招く恐れがあります。

そのため、副作用は useEffect を使って注入します。

useEffect(didUpdate);

useEffectrender 処理が走る度に引数に渡された関数 didUpdate を呼び出します。

ただ、特定条件下でのみ didUpdate を呼び出したい場合は、第 2 引数に変更を監視する値を指定します。

useEffect(
  () => {
    const subscription = props.source.subscribe();
    return () => {
      subscription.unsubscribe();
    };
  },
  [props.source],
);

Conditionally firing an effect より引用)

上記のコードでは、props.source に変更があったときだけ useEffect 内の関数が実行されます。

初回の render 処理時のみ関数を実行させたい場合は、第 2 引数に空の配列 [] を渡してください。

Hooks の制約

Hooks の利用にはいくつか制約があります。

トップレベルで呼び出す必要がある

  • ループ内
  • 条件式内
  • ネストした関数内

で Hooks を呼び出してはいけません。

関数コンポーネント内でのみ呼び出す必要がある

通常の JavaScript 関数内で呼び出しは行わないでください。Hooks は関数型コンポーネント内でのみ利用できます。

まとめ

Hooks は関数型コンポーネントにステートフルなロジックを組み込むための仕組みです。

非常にシンプルな仕組みで実現できる分、様々な応用が可能です。次回以降、発展的な内容も見ていきたいと思います。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

19 − thirteen =