思い込みで突破

インフラエンジニアぽい人の雑記

React Hooks 副作用フックに入門してみた

今回はReact Hooksの副作用(effect)フックについて学習しました。 前回はこちら。 tosyan-samoarinan.hatenablog.com

今回は以下のReact公式ドキュメントを参考にしています。 ja.reactjs.org

実装サンプル

まずReactのドキュメントをもとに実装しました。 タブに表示されるタイトルにカウントが表示されました。  f:id:tosyan_samoarinan:20191203230926p:plain

import React, { useState, useEffect } from 'react';
import './App.css';

function App() {
  const [count, setCount] = useState(0);
  const [second_count, setSecondCount] = useState(0);
  const [second_text, setCountText] = useState({text:"Help! Push Second Button"});

  const onclickFunction = () => {
    setSecondCount(second_count + 1);
    setCountText({text:"Thank you for putting button!!!!"})
  }

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

  return (
  <div className="App">
      <header className="App-header">
        <p>You clicked {count} times</p>
        <button onClick={() => setCount(count + 1)}>
          Click me
        </button>
        <p>
          {second_text.text}.
          Yout clicked {second_count} times
        </p>
        <button onClick={onclickFunction}>
          Click me.Second button
        </button>
      </header>
    </div>
  );
}

export default App;

副作用フックとは

そもそも副作用とは、ですが以下の記事が詳しいです。 英語でside effectです。

  1. 引数以外の要因で結果が変わってしまう関数
  2. 関数の外に影響を与えてしまう関数
    (中略)
    Reactにおいて具体的にはどのような処理が副作用であるかというと DOMを変更する APIとの通信 console.log setState() 変数への代入 などです。 Reactにおける「副作用」とは? - Qiita

まずは関数コンポーネント外にも影響を与えるものと理解しました。 サンプルプログラムでは、document.titleを変更する際に利用しています。 DOMはドキュメントオブジェクトモデルの略でHTML、XMLの要素を操作する仕組みです。 document.titleはHTMLのtitleタグのため、DOMを変更するのに副作用フックを使用しています。

クリーンアップを必要としない副作用

クラスを使った例は以下の通りです。(ドキュメントと同じです)

import React from 'react';
import './App.css';

class App extends React.Component {
  constructor(props){
    super(props);
    this.state = { 
      count: 0
    };
  }
  
  componentDidMount(){
    document.title = `You clicked ${this.state.count} times`;
  }
  componentDidUpdate(){
    document.title = `You clicked ${this.state.count} times`;
  }
  
    render(){
      return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1})}>
          Click me
        </button>
      </div>
      );
    }
  }
export default App;

f:id:tosyan_samoarinan:20191204232937p:plain

Reactドキュメントのサンプルプログラムでは、クラスの場合componentDidMount,componentDidUpdateを使用しており、クラスコンポーネントのrenderメソッドでは副作用を起こすべきではない、副作用を起こすのは早すぎる、とあります。 文章だとよくわかりませんが、以下のコンポーネントのライフサイクル図を見て何となくイメージができました。 なお、マウント(mounting)は最初にDOMが描画されるときです。 DOMが削除されるときをアンマウント(unmounting)と呼びます。

React lifecycle methods diagram

ここでの問題点は、最初のレンダリングと更新時で別のメソッドが呼び出されるため、2か所で同じメソッドを呼び出さなければいけないことです。

useEffect フックは componentDidMount と componentDidUpdate と componentWillUnmount がまとまったもので、renderの後に処理されます。 ReactはuseEffectに渡した関数を覚えていて、これを副作用と呼んでいます。 useEffectはコンポーネント内に記述するのでstateにアクセス可能です。 

 

クリーンアップを必要とする副作用

こちらについては、サンプルプログラムが単体では動作しないものだったので今回は割愛します。 クリーンアップを必要とする副作用は関数を返します。 サンプルプログラムはcleanup関数を返していましたが、別名でも名前なし(アロー関数)でも問題ありません。

何らかの外部のデータソースへの購読をする場合、マウント時にセットアップし、アンマウント時にメモリリークが発生しないようにクリーンアップが必要です。  (今回プログラムを作成して動かせませんでしたが、Java等でDB接続するときのオープン、クローズをイメージしてます。) クラスを使用する場合は、componentDidMountでデータ購読、componentWillunmountでクリーンアップします。 フックを使用する場合は、useEffectで購読とクリーンアップを一緒に書けます。 useEffectを実行後、Reactはクリーンアップのタイミングが来たら、実行します。 useeffectは1回だけでなく毎回レンダー時に実行されるため、次の副作用を実行する前にもクリーンアップします。

参考資料

副作用フックの利用法 – React

JavaScript初心者でもすぐわかる!DOMとは何か?

React コンポーネントのライフサイクルとメソッドの役割について - Qiita