BETA

とにかくReact Hooks入門ハンズオン

投稿日:2018-11-06
最終更新:2018-11-07
※この記事は外部サイト(https://mrsekut.site/?p=3206)からのクロス投稿です

最近、世間を賑わせているReact Hooksですが、とりあえず細かいことは置いといて手を動かして試してみます。

環境構築

完成品はこちら

React Hooksはまだ仕様策定中で、既存のプロジェクトに入れたくないので0から構築します。
といっても、create-react-appで。
プロジェクト名は「TrialReactHooks」とでもします。

$ mkdir TrialReactHooks
$ cd TrialReactHooks

create-react-appをインストール。
$ npx create-react-app trial-react-hooks $ cd trial-react-hooks

v16.7.0-alphaのReactを入れます。
$ yarn add -D [email protected] [email protected]

linterもすでに用意されているのでこれも入れてしまいましょう。
$ yarn add -D [email protected]

.eslintrc.jsを作ります。

'use strict';

module.exports = {
  plugins: ['react', 'react-internal', 'react-hooks'],
  rules: {
    'react-hooks/rules-of-hooks': 'error'
  }
};

とりあえずはこれで環境が整いました。
$ yarn start で立ち上がります。

これからReact Hooksを触っていきます。

useStateを使う

公式: Using the State Hook React

最もシンプルなものです。
これまではstateを扱うときは、(とりあえずは)Classを使ってコンポーネントを作る必要がありましたが、hooksを使うと関数コンポーネントで以下のように簡潔に書くことができます。

以下は本家のサイトのものとほぼ同じものです。

// src/comopnents/TrialUseState/index.jsx
import React, { useState } from 'react';

export const TrialUseState = ({ initialValue }) => {
  const [count, setCount] = useState(initialValue);
  return (
    <>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>click me</button>
    </>
  );
};

このコンポーネントをApp.jsxに読み込みます。

// App.jsx
import * as React from 'react';
import './App.css';

import { TrialUseState } from './components/TrialUseState/index';

class App extends React.Component {
  render() {
    return (
      <div className="App">
        <TrialUseState initialValue={0} />
      </div>
    );
  }
}

export default App;

これで完了です。
数字をインクリメントするコンポーネントが完成しました。
とりあえずブラウザで試してみると直感の通りの動きをするのがわかります。

7行目の以下の部分。
ここで、useState()関数を使用し、その2つの返り値を分割代入しています。

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

この関数は、現在のstateとそのstateを更新する関数を返します。
括弧の中身はそのstateの初期値になります。

例えばトグルするbooleanの例。
さっきのを少し書き換えてみます。

// src/components/TrialUseState/index.jsx
export const TrialUseState = () => {
    const [flg, setFlg] = useState(false);
    return (
      <>
        <p>flg: {flg ? 'true' : 'false'}</p>
        <button onClick={() => setFlg(!flg)}>click me</button>
      </>
    );
};

とても簡単にstateを利用することができました。
ドキュメントを読む限り、さきほどの例と同様にuseStateの2つの返り値は「hoge」と「setHoge」のように名付けるのが良さそうです。

ちなみに、最初の例の数字をインクリメントするものを従来のclassを使った書き方をすると、以下のようになります。

export class TrialUseState extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };

    this.setCount = this.setCount.bind(this);
  }

  setCount() {
    this.setState({
      count: this.state.count + 1
    });
  }

  render() {
    return (
      <>
        <p>You clicked {this.state.count} times</p>
        <button onClick={this.setCount}>click me</button>
      </>
    );
  }
}

だいぶ厳つさが増しますね。

class型のコンポーネントはstateを管理したり、Reduxと接続したり、lifecycleメソッドを利用するときに必須でした。

これまでもできるだけSFCで書けるようにするために高階関数ならぬ高階コンポーネント(HoC)を使ったりして再利用性を高める方策を取っていました。

ですが、このhooksが導入されたことによりわざわざ外部モジュールに頼らずとも再利用性の高いコンポーネントを作ることができるようになりました。

HoCを実現するための便利モジュール郡の代表にrecomposeがありましたが、Hooksの発表により役目を終えたということで開発が終了されました。
【参考】acdlite/recompose: A React utility belt for function components and higher-order components.

useEffectを使う

公式: Using the Effect Hook React

次にuseEffectを使ってみます。
これはデータのfetchやDOM操作など、レンダリングの前後に行う副作用の処理を書くときに使うものです。

// src/components/TrialUseEffect/index.jsx
import React, { useState, useEffect } from 'react';

export const TrialUseEffect = () => {
  const [flg, setFlg] = useState(false);

  useEffect(
    () => {
      document.title(`you checked ${flg}`);
    }
  );

  return (
    <>
      {console.log('rendering')}
      <p>flg: {flg ? 'true' : 'false'}</p>
      <button onClick={() => setFlg(!flg)}>click me</button>
    </>
  );
};

従来、class内で使われていたcomponentDidMount,componentDidUpdate, componentWillUnmountが一つのAPIであるuseEffectにまとめられたようです。

デフォルトではEffectはレンダリング後に実行されますが、return句を書くとunmount時に実行する関数を書けます。
以下のようにconsole.logを埋め込むことで確認できます。

const TrialUseEffect = () => {
  const [flg, setFlg] = useState(false);

  useEffect(() => {
    console.log('before');
    return () => console.log('after');
  });

  return (
    <>
      {console.log('rendering')}
      <p>flg: {flg ? 'true' : 'false'}</p>
      <button onClick={() => setFlg(!flg)}>click me</button>
    </>
  );
};

これの何が嬉しいのかというと、これまでcomponentDidMountでなにかのAPIのsubscribeを開始し、componentDidUpdateでunsubscribeするような処理に使われていました。
これらはほぼ同じような処理にもかかわらず、2つに分けて記述する必要がありました。

これをuseEffectを使うことで密接した場所に書くことができ、可読性の高いコードを書けるようになります。

たしかに。これはclassよりも強力ですね・・。
React開発チームは完全にclassなしでも記述できるように向かっているようです。

パフォーマンスを気にするなら

参考: https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects

実はこのuseEffect関数は第2引数を取ることができ、ここにstateを記述すると再レンダリング時にそのstateが更新されていればこの関数内の処理が走るように最適化できます。

useEffect(
  () => {
    console.log('after rendering');
    return () => console.log('after');
  },
  [flg]
);

これは従来のshouldComponentUpdateにも似てますね。

Custom Hooksを知る

公式: Building Your Own Hooks React

あと1つ触ってみます。
公式のところには何かいろいろ書いてありますが、とりあえずはこれらのhooksはjsxを返すもの関数(コンポーネント)以外にも使えるということです。

以下のコードのように、返り値がJSXではなくstring型の関数を定義していますが、この中でもuseEffectを使用することができます。

const useEffectFunctoin = num => {
  useEffect(() => {
    console.log('before');
    return () => console.log('after');
  });
  return num >= 10 ? 'Finish' : 'Loading...';
};

この関数はいつもと同じように以下のように使うことができます。

const TrialCustomHooks = ({ initialValue }) => {
  const [count, setCount] = useState(initialValue);
  const num = useEffectFunctoin(count);

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

こうすれば10より小さい間は「Loading...」と出て、それより大きくなると「Finish」と表示されます。
これは一件普通の関数定義と変わらないですが、console.logを覗くと描画の前後に出力があることがわかります。

このようにすることで、コンポーネントだけでなくステートフルな「ロジック」の部分も再利用することができるようになります。

これまでもHoCを使って「UI部分」と「ロジック部分」のコンポーネントを組み合わせるAtomicDesignなどの開発の仕方がありましたが、これまたHoCを使わずに実現できるようになりました。

このように、hooksを内部で使う関数の名前を「use」から始めて「useなんちゃら」関数と命名したもののことを「Custom Hooks」と呼びます。

この慣習に習って命名することでlinterがhooksの利用を検知し、助けを得ることができるようにみたいです。

最後に

こんな感じでとりあえずは手を動かして、大まかな挙動が確認できました。

これで、公式の「Hooks at a Glance」、「Using the State Hook」、「Using the Effect Hook」に書いてあることは一通りやりました。(少し省略していますが)

これからは、いろいろな記事や公式ページを確認して細かいことを確認していけたらなと思います。

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@mrsekutの技術ブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
or 外部アカウントではじめる
10秒で技術ブログが作れます!