One Step Ahead

プログラミングやエンジニアリング全般について書いていきます

React Effect Hooksの覚え書き(基本)

はじめに


Effect Hooks はReact16.8から導入された機能です。
Function Component に対して、Class ComponentのcomponentDidMountcomponentWillUnmountなどに相当する処理を実装する場合に使用します。

useEffectを使うたびにcomponentDiMountcomponentWillUnmountの使い分けについて調べている気がしたので、いい加減まとめておきます。

React Component の Lifecycle


React の Lifecycleは大まかにMount, Update, Unmountのサイクルに大別されます。 f:id:EaE:20210118150715p:plain

Mountサイクル

ComponentのインスタンスがDOMに挿入される際のサイクルになります。

Updateサイクル

PropsState の変更によって再レンダリングが発生する場合のサイクルになります。

Unmountサイクル

Componentが DOM から削除されるときのサイクルになります。

各VersionにおけるLifecycleを確認する場合には、公式で紹介されているこちらのサイトが便利です。

Effect Hooksが与える副作用


これについては、公式サイトに名言されています。

If you’re familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.

--- Reactクラスのライフサイクルメソッドに慣れている方は、useEffect HookをcomponentDidMount、componentDidUpdate、componentWillUnmountを組み合わせたものと考えることができます。---

Class components における componentDidMountcomponentDidUpdatecomponentWillUnmountの役割を担うことができます。

公式サイトのサンプルコードを参考に、 componentDidMountcomponentDidUpdatecomponentWillUnmountを実装したClassとFunction Componentを↓に記載します。
やっていることは少し奇妙ですが、ButtonをClickすることでドキュメントタイトルを切り替えています。

import React from 'react';
import ReactDOM from 'react-dom';

interface Props {}
interface State {
  count: number;
}
class ClassComponent extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = {
      count: 1
    }
  }

  componentDidMount() {
    document.title = `Called MountFunc!!`;
  }

  componentDidUpdate() {
    document.title = `Called UpdateFunc!! Cnt = ${this.state.count}`;
  }

  componentWillUnmount() {
    console.log("Called UnmountFunc");
  }

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

  render() {
    return (
      <div>
        <h1>Counter</h1>
        <button onClick={_ => this.setCount()}>Click</button>
      </div>);
  }
}

ReactDOM.render(
  <React.StrictMode>
    <ClassComponent />
  </React.StrictMode>,
  document.getElementById('root')
);

Class Componentの場合は、componentDidMountcomponentDidUpdatecomponentWillUnmountをそれぞれ実装することで、Lifecycleに応じて処理を実行することができます。

import React, { useEffect, useState } from 'react';
import ReactDOM from 'react-dom';

interface Props {}

function FunctionComponent(props: Props) {
  const [count, setCount] = useState(1);
  const initState = 1;

  useEffect(() => {
     // componentDidMount &  componentDidUpdate
    if (initState === count) {
      // componentDidMount専用
      document.title = `Called MountFunc!!`;
    } else {
      // componentDidUpdate専用
      document.title = `Called UpdateFunc!! Cnt = ${count}`;
    }   

    // 戻り値として、componentWillUnmount
    return console.log("Called UnmountFunc");
  }) 

  return (
    <div>
      <h1>Counter</h1>
      <button onClick={_ => setCount(count + 1)}>Click</button>
    </div>
  );
}

ReactDOM.render(
  <React.StrictMode>
    <FunctionComponent />
  </React.StrictMode>,
  document.getElementById('root')
);

Function Componentの場合は、useEffectだけでcomponentDidMountcomponentDidUpdatecomponentWillUnmountを表現する必要があります。
componentDidMountcomponentDidUpdateはuseEffectに引き渡す関数本体に処理を記載することで表現できます。 今回は、MountサイクルとUpdateサイクルを分けて表現したかったので、初期ステータスと比較して呼び出す処理を変更しています。

componentWillUnmountは、useEffectの戻り値として、関数を返却することで表現することができます。

単純に使うだけであればこれだけOKですが、レンダリング毎のuseEffectの抑制や、Mount & Unmount時のみの実行などチューニングを行う場合には、より複雑になるのでそれはまた次の機会にします。

まとめ


  • useEffectを使うことで、関数コンポーネントでもcomponentDidMountcomponentDidUpdatecomponentWillUnmountに相応する作用をもたらすことができる。
  • useEffectを使うことで、より細かなチューニングも行うことができる。

参考・引用