nomurabbitのブログ

nomurabbitのブログはITを中心にした技術ブログです。

nomurabbitのブログ

【React】あーそーゆーことねuseEffect完全に理解した【Hook】

この記事は
Calendar for React | Advent Calendar 2021 - Qiita
の13日目です。

12日目は@sonishimuraさんのこちらの記事でした。
zenn.dev
警告文とか結構無視して
開発続けちゃいがちなので、
勉強して改善させていただきます!

改めましてこんにちは!nomurabbitです。
この記事では、ReactのHookのひとつ、
useEffectについて紹介したいと思います。

React Hookって
イメージをつかむのが難しいですよね。
一緒に勉強していきましょう!

useEffectと実行タイミング

useEffectの概要を把握するために、
実行タイミングから理解するのがよいと思います。

Reactはレンダリング、差分計算、DOMの更新という
3つのプロセスを経て画面を表示しますが、
useEffectはDOMの更新のさらに後に実行されます。

例えばこんなプログラムを実行すると…

import React, { useEffect, useState } from 'react';
import ResponsiveDrawer from '../components/layout/ResponsiveDrawer';

const Home = () => {

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

  useEffect(() => {
    //document.title = "effect";
  });

  document.title = "rendering";
  
  return (
    <ResponsiveDrawer>
      <div>
        <p>It's amplify test / home page.</p>
      </div>
    </ResponsiveDrawer>
  );
}
  
export default Home;

実行結果はこんな感じ。
ページのタイトルは rendering となります。

f:id:nomurabbit:20211212224424p:plain

次にuseEffectのコメントアウトを外して
実行してみると…。

import React, { useEffect, useState } from 'react';
import ResponsiveDrawer from '../components/layout/ResponsiveDrawer';

const Home = () => {

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

  useEffect(() => {
    document.title = "effect";
  });

  document.title = "rendering";
  
  return (
    <ResponsiveDrawer>
      <div>
        <p>It's amplify test / home page.</p>
      </div>
    </ResponsiveDrawer>
  );
}
  
export default Home;

このように
ページのタイトルは effect となります。

f:id:nomurabbit:20211212224602p:plain

useEffectの実行タイミングについて、
なんとなくおわかりいただけたでしょうか?

useEffectの利用法

useEffectの目的が
実行タイミングをずらすだけなら、
ソースの最後に記述すればいいのでは?

import React, { useEffect, useState } from 'react';
import ResponsiveDrawer from '../components/layout/ResponsiveDrawer';

const Home = () => {

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

  document.title = "rendering";

  document.title = "effect";
  
  return (
    <ResponsiveDrawer>
      <div>
        <p>It's amplify test / home page.</p>
      </div>
    </ResponsiveDrawer>
  );
}
  
export default Home;

f:id:nomurabbit:20211212225900p:plain

はい、その疑問はごもっともです。

この例では実行結果は同じになりますね。
useEffectを使っても使わなくても
ページのタイトルはeffectです。

ここで今一度、
公式のチュートリアルを見てみましょう。

ja.reactjs.org

副作用 (effect) フック により、
関数コンポーネント内で副作用を実行することが
できるようになります。

とあります。言い換えると、

関数コンポーネント内で副作用を実行するときは
useEffectを使いましょう。

ということです。
では、ここでいう副作用とは何のことでしょうか?

残念ながらReactのチュートリアルからは
副作用の定義は読み取れませんでした。

しかし、一般的に副作用というと、
関数型の世界で下記のような概念で
語られることが多いと思います。

  1. 関数の外に影響を与えるもの
  2. 関数の引数以外で、戻り値に影響を与えるもの

ページのタイトルを変更する例は1.に該当します。
また、関数コンポーネントからAPIを呼び出す場合は
2.に該当します。

これら副作用を
事故なく実行するための仕組みが
useEffectだと理解できます。

useEffectの使用例

ここからはuseEffectの使用例をいくつかあげていきます。

まずはDOMの書き換えです。
関数の外に影響を与える副作用に該当します。

import React, { useEffect, useState } from 'react';
import ResponsiveDrawer from '../components/layout/ResponsiveDrawer';

const Home = () => {

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

  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
  });
  
  return (
    <ResponsiveDrawer>
      <div>
        <p>It's amplify test / home page.</p>
        <p>You clicked {count} times</p>
        <button onClick={() => setCount(count + 1)}>
          Click me
        </button>
      </div>
    </ResponsiveDrawer>
  );
}
  
export default Home;

f:id:nomurabbit:20211212233423g:plain

※少しわかりにくいですが、
ページのタイトルが更新されています。

次にAPIの呼び出し(UPDATE系)です。
こちらも関数の外に影響を与える副作用に該当します。

import React, { useEffect, useState } from 'react';
import axios from 'axios';
import ResponsiveDrawer from '../components/layout/ResponsiveDrawer';

const Home = () => {

  const [msg, setMsg] = useState("");

  const apiUrl = "";

  useEffect(() => {
    if(msg !== ""){
      axios.post(apiUrl, {
        message: msg
      },{
        headers: {
          'Content-Type': "text/plain"
        },
      })
      .then(function (response) {
        console.log(response);
      })
      .catch(function (error) {
        console.log(error);
      });
    }
  },[msg]);
  
  return (
    <ResponsiveDrawer>
      <div>
        <p>It's amplify test / home page.</p>
        <button onClick={() => setMsg("Hello nomurabbit")}>
          Click me
        </button>
      </div>
    </ResponsiveDrawer>
  );
}
  
export default Home;

最後にAPIの呼び出し(SELECT系)です。
こちらはAPIの結果で関数の戻り値が変わる可能性があるので、
関数の引数以外で、戻り値に影響を与えるもの副作用に該当します。

import React, { useEffect, useState } from 'react';
import axios from 'axios';
import ResponsiveDrawer from '../components/layout/ResponsiveDrawer';

const Home = () => {

  const [strikeout, setStrikeout] = useState("");
  const [walks, setWalks] = useState("");
  const [bbk, setBbk] = useState("");

  const apiUrl = "";

  interface IResponse {
    bbk: string
  }

  useEffect(() => {
    if(strikeout !== "" && walks !== ""){
      axios.post<IResponse>(apiUrl , {
        "strikeout": strikeout,
        "walks": walks
      },{
        headers: {
          'Content-Type': "text/plain"
        },
      })
      .then(function (response) {
        setBbk(response.data.bbk);
        console.log();
      })
      .catch(function (error) {
        console.log(error);
      });
    }
  },[strikeout, walks]);
  
  return (
    <ResponsiveDrawer>
      <div>
        <p>It's amplify test / home page.</p>
        <p>bbk is {bbk}</p>
        <button onClick={() => {setStrikeout("60"); setWalks("42");}}>
          Click me
        </button>
      </div>
    </ResponsiveDrawer>
  );
}
  
export default Home;

f:id:nomurabbit:20211213004424g:plain

以上がuseEffectについての紹介となります。
なんとなくの概念だけでも
ご理解いただけましたでしょうか?

よかったら参考にしてみてください。

13日目に@ut0nさんの記事もアップされてます。
techtekt.persol-career.co.jp
流行りについていけてないなーとか
感じることがとくあるので、
こういうまとめ記事とても助かります!

14日目は@7tsunoさんの記事です。
よろしくお願いします!