kakakakakku blog

Weekly Tech Blog: Keep on Learning!

React Hooks 入門 : useEffect() を試す

React 16.8 で追加された新機能「React Hooks」は React の機能を効率的に使えるようにする仕組みで,具体的には useState()useEffect() などを使う.日本語化されたドキュメントが非常に読みやすく,以下のドキュメントを読むと React Hooks の概要を学べる.特に「1. フックの導入」を読むと,どんな課題を解決するために React Hooks が登場したのか?という歴史的な背景を学べる.

ja.reactjs.org

また「2. フック早わかり」を読むと,よく使う React Hooks の機能と Custom Hooks の紹介もある.そして「なぜ Hooks という名前なのか?」という意味も学べる.hook into と書かれていて,なるほどー!と思った.

フックとは、関数コンポーネントに state やライフサイクルといった React の機能を “接続する (hook into)” ための関数です。

ja.reactjs.org

副作用フック : useEffect() を試す

ja.reactjs.org

今回は「4. 副作用フックの利用法」を読みながら useEffect() を試す.まず,ドキュメントに載っているコードを create-react-app と TypeScript で書き直すと,以下のようになる.React Hooks 的には count State を useState() で定義し,さらに count State をタイトルに反映する useEffect() を定義している.

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

const App: React.FC = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

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

export default App;

ボタンを押すと,画面とタイトルを更新できる.React コンポーネント内にある DOM を更新したり,外部データを取得したりすることを「副作用」もしくは「作用」と表現し,英語では「side-effect」もしくは「effect」と表現する.

f:id:kakku22:20191022014857p:plain

クリーンアップ

従来 React でクリーンアップをするために実装していたライフサイクル componentWillUnmount() を React Hooks で実装する場合,useEffect() から関数を返せば良く,シンプルな実装になる.以下のように return で関数を返す.ドキュメントに載っている通り cleanup() 関数を実装して返しても良いし,アロー関数を返すこともできる.

useEffect(() => {
  document.title = `You clicked ${count} times`;
  return (() => console.log('Clean Up 🐤'));
});

useEffect() は画面描画時(初回 render)と画面更新時に実行される.よって render 後に副作用が起きることになり,クリーンアップも毎回実行されることになる.実際にクリーンアップを追加した状態でボタンを押すと,コンソールに繰り返しログが出力されていた.

Clean Up 🐤
Clean Up 🐤
Clean Up 🐤
Clean Up 🐤
Clean Up 🐤

副作用のスキップ

ドキュメントを読み進めると,後半に「ヒント」として「副作用のスキップによるパフォーマンス改善」というトピックがある.render ごとに副作用を起こすとパフォーマンスに影響するため,render 時に「特定の値が更新されていない場合は副作用をスキップできる」構文がサポートされている.

以下のように useEffect() の第2引数に配列を指定する.今回は [count] だけど,[count, count2, count3] のように複数の State を指定することもできる.すると,第2引数に指定した値が更新されたときのみ副作用が起きるように抑制できる.

useEffect(() => {
  document.title = `You clicked ${count} times`;
  return (() => console.log('Clean Up 🐤'));
}, [count]);

副作用を1回だけ起こす

例えば,画面描画時(初回 render)に API を実行して,その後は副作用を起こさないように抑制する場面もある.その場合は useEffect() の第2引数に空配列 [] を指定すると「何の値にも依存しない」ことを意味することになり,結果的に副作用を1回だけ起こせるようになる.

実際に試すため.まず axios を追加し,useEffect() から random.cat API を実行する.ランダムに返ってくる猫画像 URL を meowUrl State に設定する.

const [meowUrl, setMeowUrl] = useState('');

useEffect(() => {
  const fetch = async () => {
    const results = await axios('http://aws.random.cat/meow');
    setMeowUrl(results.data.file)
  };
  fetch();
}, []);

取得した画像を表示すると,期待通りの動作になった.JSX の部分は GitHub 参照!

f:id:kakku22:20191022013733p:plain

試しに useEffect() の第2引数を削除すると,無限ループになり API を繰り返し実行してしまうため,第2引数に空配列 [] を指定する重要さを確認できた.無駄なリクエストを送り続けないように気を付けよう.

f:id:kakku22:20191022013746p:plain

まとめ

  • 最近「React Hooks」を勉強している
  • ドキュメントを読みながら useEffect() を試した
  • ライフサイクル/クリーンアップ/スキップなど,理解しておくべき仕様を学べた
  • useEffect() の第2引数に空配列 [] を指定すると「何の値にも依存しない」ことを意味する
  • サンプルコードは GitHub に置いておいた

github.com