LoginSignup
7
0

【React】初学者向け Reactフック(hook)について知ろう

Last updated at Posted at 2023-12-20

はじめに

 現在業務でReactでの開発をしています。初めて触ったのは半年ほど前でした。Reactを学ぶ上でフックを理解することが結構重要だなと感じたので、今回初学者向けにReactのフックについてまとめてみようと思います。

対象読者

  • Reactを学び始めたばかりの方
  • フックの概要をざっと理解したい方
  • そもそもフックってなんぞや、という方

では、さっそく本題に入ります↓

フック(hook)とはなにか?

フックを使うと、コンポーネントの状態を管理したり、状態変化に応じた処理を実装することができます。

※そもそもは、もともと状態を管理したりライフサイクルに応じた処理を実装するにはクラスを作成する必要がありましたが、それを関数コンポーネントで出来るようにしたよ、というのがフックです。ただ、これから学び始める人にとってはあまり関係ない話(のように感じる)ので、一旦は↑で書いたような機能のことなんだなーという理解でよいと思います。

概要を説明されるよりも、具体例を見た方が実感がわくと思いますので、早速具体例を見ていきましょう。

フックには色々な種類がありますが、本記事では基本的なものをいくつかピックアップして説明します。

基本のフック1 : useState

一番基本的なフック、useStateです。これは、状態を管理するためのフックです。
今回は、「ボタンが押された回数(=状態)を管理する」ということをやってみます。

index.tsx
import React, { useState } from 'react';

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

  const handleClick = () => {
    setCount(count + 1);
  };

  return (
    <>
      <button onClick={handleClick}>ボタン</button>
      <p>{count}回押しました</p>
    </>
  );
};

export default Counter;

こちらは非常にシンプルなコンポーネントです(出力は想像してください)。
ボタンと、「n回押しました」という表示が出るだけですね。

画面起動時、最初は「0回押しました」と表示されていて、ボタンを押すと押した回数が1ずつ増えていきます。

一つずつ分解してみてみましょう。

index.tsx
return (
    <>
      <button onClick={handleClick}>ボタン</button>
      <p>{count}回押しました</p>
    </>
  );

まずこちらは出力です。

  • 「ボタン」と書かれたボタンがある。
  • ボタンを押すと、handleClickという関数が実行される。
  • countという変数の値が表示される
    という感じです。

続いて、一番重要な、useStateを使った部分を見ていきます。

index.tsx
// [状態を格納する変数、状態を更新する関数] = useState(状態の初期値);
const [count, setCount] = useState(0);

useStateを使うと、状態を格納する変数と、状態を更新する関数を生成できます。
今回の場合は、countに「何回押されたか」という状態が保存され、setCountを使ってcountを更新する、ということです。
useStateの引数には初期値を設定できます。今回は0回からスタートさせたいので0にしました。

最後にhandleClickの中身を見てみましょう。

index.tsx
const handleClick = () => {
    setCount(count + 1);
  };

ボタンが押されるとこのhandleClickが実行されます。
setCountの引数に、更新したい値を入れてあげます。
今回は、一回実行されるごとに今の状態より1大きい数値で更新したいので、count+1 を代入してあげればよいですね。

以上が、useStateの基本的な動作になります。

基本のフック2 : useEffect

つづいて、useEffectについて説明します。

useEffectは、コンポーネントのレンダリング後や、状態が変化した時に動作する処理を実装できます。

今回は、useEffectを使って先ほどのコードを改造します。押した回数が10回以上になったらボタンが赤くなるようにしてみましょう。

index.tsx
import { useEffect, useState } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0);
  const [buttonStyle, setButtonStyle] = useState({});

  const handleClick = () => {
    setCount(count + 1);
  };

  useEffect(() => {
    if (count >= 10) {
      setButtonStyle({ backgroundColor: 'red' });
    }
  }, [count]);

  return (
    <div>
      <button onClick={handleClick} style={buttonStyle}>
        ボタン
      </button>
      <p>{count}回押しました</p>
    </div>
  );
};

export default Counter;

こちらも順に見ていきましょう。

押さえておくべきなのは、useEffectの中の処理が、

  • レンダリング後
  • count変更時
    に実行されるということです。
index.tsx
<button onClick={handleClick} style={buttonStyle}>
   ボタン
</button>

まず、今回はボタンの色を変える必要があります。そこで、styleをbuttonStyleという変数に持たせて、buttonStyleをボタンに渡すことにします。

index.tsx
  const [count, setCount] = useState(0);
  const [buttonStyle, setButtonStyle] = useState({});

useStateを使って二つの状態を管理します。
一つは、先ほどと同様、ボタンが押された回数。
もう一つは、ボタンにあてるstyleを管理します。最初は何のスタイルもあてないので、空のオブジェクトを初期値として渡しています。

index.tsx
  useEffect(() => {
    if (count >= 10) {
      setButtonStyle({ backgroundColor: 'red' });
    }
  }, [count]); // countが更新されると実行される(もしここが空なら、レンダリング後だけ実行される)

本題のuseEffectの部分です。
useEffectは、レンダリング後や状態変化後に実行されます。
最終行に、countという変数が渡されていますね。これは、countの状態が変化したらこのuseEffectを実行してね、という意味です。[] のように空にしておけば、レンダリング後しか実行されません。
useEffectの中身はシンプルです。もしcountが10以上になったら、背景色をredにするstyleをbuttonStyleに渡してあげるだけです。おさらいですが、buttonStyleの状態を更新するにはsetButtonStyleを使ってあげればいいですね。

こうすると、countが更新されるごとにuseEffectが実行され、countが10以上になったときは背景色をredにするスタイルが渡され、ボタンが赤くなるのです。

基本のフック3 : useContext

続いて、useContextについて説明します。

useContextを使うと、親コンポーネントから子コンポーネント、孫コンポーネント、それ以下への値の受け渡しを簡単に行うことができるようになります。グローバル変数のようなイメージです。今回は、親コンポーネントから孫コンポーネントに値を渡すということをやってみます。

それではまず、ParentComponent, ChildComponent, GrandChildComponentという名前のコンポーネントを作ります。

ParentComponent.tsx
import React, { useState } from "react";
import ChildComponent from "./ChildComponent";

export const GlobalColorContext = React.createContext('red');

const ParentComponent = () => {
    const [globalColor, setGlobalColor] = useState('red');

    return(
    <GlobalColorContext.Provider value={globalColor} >
        <ChildComponent />
        <div style={{margin:"0 30px", width: "260px"}}>
            <button 
                onClick={()=>{setGlobalColor('red')}}
                style={{width:"130px"}}
            >
                RED
            </button>
            <button 
                onClick={()=>{setGlobalColor('blue')}}
                style={{width:"130px"}}
            >
                BLUE
            </button>
        </div>
    </GlobalColorContext.Provider>
    );
};

export default ParentComponent;
ChildComponent.tsx
import React from "react";
import GrandChildComppnent from "./GrandChildComponent";

const ChildComponent = () => {
    return(
        <>
        <div style={{padding: "30px",
            margin: "30px",
            width: "200px",
            backgroundColor: "grey"
        }}>
            <GrandChildComppnent />
        </div>
        </>
    );
}

export default ChildComponent;
GrandChild.tsx
import React, { useContext } from "react";
import { GlobalColorContext } from "./ChangeTheme";

const GrandChildComppnent = () => {
    const globalColor = useContext(GlobalColorContext);

    return(
        <>
        <div style={{ backgroundColor: globalColor,
            padding: "30px",
            margin: "0 auto",
            width:"100px",
            color: "white"}}>
            GrandChild
        </div>
        </>
    );
}

export default GrandChildComppnent;

表示はこんな感じになります ↓
image.png
image.png

やっていることは、

  • ParentComponentに二つボタンを用意する
  • ParentComponent内でChildComponentを呼び出す
  • ChildComponent内でGrandChildComponentを呼び出す
  • 各ボタンを押すと、GrandChildComponentの色が変わる
    という感じです。

順番に見ていきます。
まずこちらが、親コンポーネントです。

ParentComponent.tsx
import React, { useState } from "react";
import ChildComponent from "./ChildComponent";

export const GlobalColorContext = React.createContext('red');

const ParentComponent = () => {
    const [globalColor, setGlobalColor] = useState('red');

    return(
    <GlobalColorContext.Provider value={globalColor} >
        <ChildComponent />
        <div style={{margin:"0 30px", width: "260px"}}>
            <button 
                onClick={()=>{setGlobalColor('red')}}
                style={{width:"130px"}}
            >
                RED
            </button>
            <button 
                onClick={()=>{setGlobalColor('blue')}}
                style={{width:"130px"}}
            >
                BLUE
            </button>
        </div>
    </GlobalColorContext.Provider>
    );
};

export default ParentComponent;

今回の肝となる、useContextの機能を使っているのはこちらです。

ParentComponent.tsx
export const GlobalColorContext = React.createContext('red');

今回は、孫コンポーネントの色を変えられるようにします。なのでまずは、孫コンポーネントの色を親コンポーネントから渡せるようにしましょう。要素を各コンポーネントに渡せるようにするためにcreateContextというメソッドを使ってContextを作成します。名前はGlobalColorContextとしました。引数にはデフォルト値を入れます。とりあえず最初は"red"を入れました。

ParentComponent.tsx
<GlobalColorContext.Provider value={globalColor} >
    <ChildComponent />
    <div style={{margin:"0 30px", width: "260px"}}>
        ...
     </div>
</GlobalColorContext.Provider>

次に、上記のように、要素を渡したいコンポーネントをGlobalColorContext.Providerで囲みます。こうすることで、GlobalColorContext.Provider内で呼び出されるコンポーネントはGlobalColorContextが持つ値を参照することができるようになります。参照させたい値はvalueに渡します。

ParentComponent.tsx
const [globalColor, setGlobalColor] = useState('red');

背景色はuseStateで管理することとし、変数名は globalColorとしました。

ParentComponent.tsx
<div style={{margin:"0 30px", width: "260px"}}>
    <button 
        onClick={()=>{setGlobalColor('red')}}
        style={{width:"130px"}}
    >
        RED
    </button>
    <button 
        onClick={()=>{setGlobalColor('blue')}}
        style={{width:"130px"}}
    >
        BLUE
    </button>
</div>

ParentComponentにボタンも用意しました。ボタンを押すと、useStateで作成したsetGlobalColorを使ってglobalColorが更新されるようにしました。

つづいて、ParentComponent内で呼び出されているChildComponentを見てみます。

ChildComponent.tsx
import React from "react";
import GrandChildComppnent from "./GrandChildComponent";

const ChildComponent = () => {
    return(
        <>
        <div style={{padding: "30px",
            margin: "30px",
            width: "200px",
            backgroundColor: "grey"
        }}>
            <GrandChildComppnent />
        </div>
        </>
    );
}

export default ChildComponent;

ここでやっていることは、GrandChildComponentを呼び出しているだけです。上に載せた画像中のグレーの部分がChildComponentにあたります。

では最後に、GrandChildComponentを見ていきます。

GrandChild.tsx
import React, { useContext } from "react";
import { GlobalColorContext } from "./ChangeTheme";

const GrandChildComppnent = () => {
    const globalColor = useContext(GlobalColorContext);

    return(
        <>
        <div style={{ backgroundColor: globalColor,
            padding: "30px",
            margin: "0 auto",
            width:"100px",
            color: "white"}}>
            GrandChild
        </div>
        </>
    );
}

export default GrandChildComppnent;

ここでuseContextが登場します。この部分です↓

GrandChild.tsx
    const globalColor = useContext(GlobalColorContext);

useContextの引数に、先ほどParentComponentで定義したContextを渡してあげると、そのContextが持つvalueを取り出すことができます。つまり、globalColorには"red"が代入されます。これで、親コンポーネントで定義した"red"を孫コンポーネントに直接渡すことができたことになります。

ここで重要なのは、親から孫へ直接渡せていることです。子コンポーネントを経由していません。変数をバケツリレーすることなく深いコンポーネントに値を渡すことが可能となります。

また、今回Contextに渡している値は、親コンポーネント内でuseStateで定義したglobalColorです。今回、ボタンを押すとglobalColorの値が"red"か"blue"に変わるようになっているので、孫コンポーネントの色もそれに応じて変化します。

基本のフック4 : useCallback

coming soon ...

終わりに

時間が無くなってしまったので、一旦今回はよく使われる3つのフックの紹介で終わりにしようと思いますが、また後日コンテンツを増やす予定です。最後までご覧いただきありがとうございました!

7
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
0