フロントエンドエンジニアをやっています。
頑張るから読んでほしい。

React Hooksの使い方

このあいだチームのバックエンドエンジニア向けにReact Hooks勉強会を開催しました。

チームのみんなでプロダクトのフロントエンドの底上げをしたくて、React Hooks啓蒙活動です!

改めてReact Hooksについて勉強&まとめることができたのでReact Hooks勉強会の内容をブログにも書いておこうと思います。

これは何?

  • Hooksとは何か?
  • Hooksを使うと何が変わるのか?
  • Hooksの使い方
  • Hooksを書くときのルール

👆についてまとめてます。

対象は

  • Reactは書けるけどHooksって何?
  • Hooksって聞いたことあるけど何がすごいの?
  • Hooks使いたいけど、よく分からないんだよね…

な人向けです。

背景

これから積極的にReact Hooksを使っていきたいなあと思っています。

でもいきなりHooks使おう!ってなっても、 Hooksって何? 使うといいことある? どういいの? 難しいの? って疑問があると思います! なのでチームのみんながHooks良さそう!使ってみようかな!って思えるようなHooksについての入門資料です。

ゴール

Hooks良さそう!使ってみようかな!って気持ちになってチームのみんながHooksで書いたPR作成できる!

Hooksって何?

クラスコンポーネント使わなくても関数コンポーネントでstateなどの機能が使えるようになるやつです!

  • useState
  • useEffect

👆を使うと関数コンポーネントでstateが使えたり、ライフサイクルメソッドっぽいことができます。

関数コンポーネントで書きたい理由

👦: Q. クラスコンポーネントで書けるのにどうして関数コンポーネントを使うの?

👨: A. 👇

  1. クラスComponentと比べてスッキリ、綺麗に書ける
    • 可読性が良くなる
  2. クラス内の this の挙動が難解
  3. 記述が冗長になりがちで、時系列が複雑なライフサイクルメソッドの挙動

これまでのReact

アプリケーションのロジックに関わるコンポーネントはクラスコンポーネント

ロジックをコンポーネント間で再利用するのが難しい

render props や higher-order componentsがあるけど、コンポーネントの再構成が必要だし、ラッパー地獄になりやすいし、可読性が低くてコードを追うのが大変です…

これからのReact

関数コンポーネントで書けるようになる

アプリケーションのロジックに関わるコンポーネント関数コンポーネントで書けるようになります!

ロジックを再利用できるようになる

Custom Hookという独自にhookを作成することを使うとロジックを再利用できるようになります。

再利用できるようになり、コードも綺麗に書けるようになります! ReactのロジックをOSSとして気軽に共有できます!

👦: Q. じゃあ全部Hooksで書いた方がいいの?

👨: A. 新しいコンポーネントはHooksで書いていくのがおすすめ

ref. https://ja.reactjs.org/docs/hooks-faq.html#should-i-use-hooks-classes-or-a-mix-of-both

クラスコンポーネントはいつかなくなるかもしれません… 何年後かのクラスコンポーネント置き換えtaskが辛くならないために新しく作成するコンポーネントは関数コンポーネントで書きたいです!

Hooks使うのは強制ではないです。無理だ!って思ったらクラスコンポーネントでも大丈夫です! でもクラスコンポーネントは将来deprecatedになると思うのでこの機会にぜひ!

Hooks

(チーム内の)プロダクトでよく使いそうなHooksたち

1. useState

関数コンポーネントで状態を持つことができるようになります!=> stateが使える

useStateの基本

const [state, setState] = useState(initialState);

👉 配列の1つ目にstate名(配列が返ってくる)、2つ目に関数(setState)、useStateの中に初期値が入ります。

こんな感じ

const [count, setCount] = useState(0)

useStateの実装

例えば、以下のようなカウンターの実装

クラスコンポーネント

関数コンポーネント hooks

💪+α useStateの中身

const state = useState(0);
console.log(state);

f:id:cidermitaina:20200429144651p:plain

console.logに出してみると以下のようなことが分かります。

  • useStateは配列を返す
  • 初期値が配列の0番目に入ってる
  • 配列の1番目にsetStateの関数っぽいのがある

2. useEffect

関数componentで副作用処理を扱う(DOMの変更、API通信)ことができます。 ライフサイクルメソッドっぽいことができます。

useEffectの基本

useEffect(didUpdate);

👉 didUpdate部分に副作用処理を扱う(DOMの変更、API通信)を書きます。

👦: Q. どのタイミングで実行されるの?

👨: A. 最初の描画時または、再レンダリング時です。

マウント時、再レンダリング時(コンポーネントの全てのstateが変更されるとき)に実行されます。

👦: Q. この値が変更したときだけ実行したい…

👨: A. useEffect の第 2 引数として、この副作用が依存している値の配列を渡します!

useEffect(() => {
    document.title = `クリック数: ${count}回`
    console.log('render')
  }, [count]) // countの値が変更されたときだけ実行されます

👦: Q. マウント時だけ実行したい…

👨: A. useEffect の第 2 引数として、空の配列を渡します!

useEffect(() => {
   mount時の処理
 }, []) // 第2引数に空の配列

useEffectの実装

f:id:cidermitaina:20200429145058g:plain

👆 mount時とcountの値が変更されるとuseEffect内の処理が実行されている

4. useCallback

パフォーマンス向上のためのフックです。 callback関数(イベントハンドラー) をメモ化します。

メモ化

  • プログラムの高速化のための最適化技法の一種
  • 関数の結果を再利用、一時的に保持する

useCallbackの基本

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b], // 依存している値の配列
);

コールバック関数と、依存している値の配列を渡します。 依存配列の要素(a, b)が変化した場合にのみメモ化した値を再計算します。

👦: Q. useCallbackするとどうなるの?

👨: A. 不要に新しく関数インスタンスを作成することを抑制し、不要な再描画を減らすことができます。

不必要なrenderを避けるためにReact.memo(stateやpropsに変更がなければ再レンダリングを行わない)等の参照の同一性を見るよう最適化されたコンポーネントにコールバックを渡す場合に便利です。

👦: Q. useCallbackしないとどうなるの?

👨: A. 全てのcomponentが再renderしてしまい、関数も再生成されてしまいます

useCallbackの実装

useCallbackしない場合 f:id:cidermitaina:20200429153024g:plain

👆 Buttonクリック時fooButtonをクリックするとbarButtonのの関数もrenderされてしまってる

useCallbackした場合 f:id:cidermitaina:20200429152921g:plain

👆 Buttonクリック時fooButtonをクリックするとfooButtonの関数のみ実行

useCallbackはReact.memoと一緒に使うと真の力を発揮するっぽい!

5. useRef

DOMを操作したり、クロージャー内で宣言された値へアクセスできます。 UIの更新に関連しない状態を管理したい場合に使用するイメージです!

useRefの基本

const refContainer = useRef(initialValue);

書き換え可能な値を .current プロパティ内に保持することができる「箱」のようなもの

useRefの実装

DOM操作

クロージャー内で宣言された値へアクセスしたい場合

💪+α 更新前のStateの値を使いたい場面

6. Custom Hooks

独自のhookを自分で作成できます。 独自のhookを作成し、コンポーネントからロジックを抽出することで、ロジックを再利用することができるようになります。

ReactのロジックをOSSとして気軽に共有できるようになったので、探してみるとたくさんOSSのReact Hooksがあります!

👇のOSSを眺めてみるとイメージしやすいかも!

https://nikgraf.github.io/react-hooks/

コードとDEMOは👇の方が探しやすいかも!

https://github.com/streamich/react-use/

(7. useMemo)

プロダクト内では使うことはなさそうなので省略します! 興味ある人は調べてね!

(8. useContext)

プロダクト内では使うことはなさそうなので省略します! 興味ある人は調べてね!

(9. useReducer)

プロダクト内では使うことはなさそうなので省略します! 興味ある人は調べてね!

Hooksのルール

Hooksを書くときに気をつけて欲しいこと&守って欲しいこと

1. Hooksはループや条件分岐の中で呼ぶことはできません!

// これはダメ
const Ng = () => {
  if (condition) {
    useEffect(() => {}, []);
  }
}

// これはOK
const Ok = () => {
  useEffect(() => {
    if (condition) {
      //
    }
  }, []);
}

2. 関数Componentの中からのみ呼べます!

Custom Hooksとして切り出した関数の中では呼ぶことはできるので注意!

3. Custom Hooksを作るときはuseXXXの命名規則に!

この名前にすることでESLint PluginなどでHooksだと認識してくれるようになります。

ESLint Plugin

HooksのESLint Pluginです。

depsの指定忘れを検知してくれます。 基本的には従っておいた方がいいです。

const App = () => {
  const foo = "...";
  const bar = "...";
  const handler = useCallback(() => {
    someFn(foo, bar);
  // fooがdepsに指定されていないのでひっかかる
  }, [bar]);
}

意図的にやっている場合やESLint Pluginが誤検知してる場合は、eslint-disableのコメントで無効にできます。

https://eslint.org/docs/user-guide/configuring#disabling-rules-with-inline-comments

まとめ

チームのプロダクトで良く使いそうなReact Hooksについてまとめてみました!

このReact Hooksについての内容はチームのみんなでプロダクトのフロントエンドを良くしたいなあと思って作ったものです。

React Hooks使いたいけどなかなか…って思ってた人や、プロダクトのフロントエンドを良くしたいなあって人の参考になれば嬉しいです。





参考