🥝

【ReactHooks - 基本フック編(3選) - 】

2024/07/22に公開

はじめに

React Hooksは、関数コンポーネントで状態管理やライフサイクルメソッドを使えるようにするための機能です。React 16.8で導入され、クラスコンポーネントを使わずにReactの機能を利用できるようになりました。以下によく扱う基本的なHooksとその使い方を説明します。

useState

関数コンポーネント内で状態を持ち、更新するための関数を返します。useStateで定義した変数が変動すると自動的に再レンダリングされるのが特徴です

-使用例-

const [変数, 変数を更新する関数] = useState(変数の初期値)
"use client"
import { useState } from "react";
const Home = () => {
    //useState
    const [value, setValue] = useState()
}
export default Home

変数と変数を更新する関数の名前は任意ですが、変数を更新する関数名は変数の先頭ににsetをつなげたものが多いです。変数の初期値はnull/0/undefined/[]/etc...と設定することが出来ます。

初期値に型を定義したい場合は、

const [value, setValue] = useState<number>()
interface stateType {
  name: string,
  mail: string
}
const [value, setValue] = useState<stateType>()

の様な書き方をします。実際に例としてuseStateを用いてボタンを押すと+1されていく処理を記載します。

page.tsx
"use client"
import { useState } from "react";
const Home = () => {
    //useState
    const [value, setValue] = useState<number>(0)
    return(
      <>
      <div className="text-center m-5">
        <button onClick={() => setValue(prev => prev + 1)} className="border border-black p-2">ボタン</button>
        <div>値: {value}</div>
      </div>
      </>
    )
}
export default Home

hooksは主にクライアントコンポーネントのみでしか使用出来ないため"use client"と記載しクライアントコンポーネント化し、stateの初期値を0としてnumber型で定義しています。

buttonのonClickで発火しsetValue(prev => prev + 1)とvalueに+1されています。
setValue(value + 1)これじゃダメなの?て思う方もいると思います。

今回の処理でいうと正常に動きますが、あまり好ましい書き方ではありません。
なぜなら下記のようなコードがある場合、1度のクリックで+2されそうに見えますが+1しか加算されません(例のためおかしな処理内容ではありますが目を瞑ってください...)

onClick={() => {
    //value = 0      
    setValue(value + 1)
    //value = 0(まだレンダリングされておらずvalueの値が変わっていないため)
    setValue(value + 1)}
    //レンダリング開始
}

useStateはstateの状態が変わるたびに再レンダリングされると書きましたが、このように更新処理が複数ある場合setValue毎にレンダリングが行われず一括にまとめて処理がされていきます。(bat処理)
そのためvalueが更新されず value = setValue(0 + 1)が2度行われているといった結果になります。

ではどの様に記述するかというと

onClick={() => {
    //value = 0 prev = 0
    setValue(prev => prev + 1)
    //value = 1 prev = 1
    setValue(prev => prev + 1)
}

となります。prevてなんやねんて感じですがuseStateのセット関数の引数は

setValue(任意の変数名 => 任意の変数名 + 1)

とする事でvalueとは違った直前の値を参照することが出来、この処理を実行すると1度のクリックで+2が加算されます。

もう一つの疑問としてsetValueで直接関数を呼び出していない事ですが
※onClick={setValue(prev => prev + 1)}ではなぜダメなのかという話

onClick={() => setValue(prev => prev + 1)}

setValueは必ずvoidを返すため、無名関数で1度挟まないとonClickにvoidを返すことになりエラーになってしまうからです。
※onCLick={}にvoidを返すことは出来ない

useEffect

関数コンポーネント内で処理の発火タイミングを制御するときに便利なHooksです

-使用例-

useEffect(() =>{
    //発火させる処理
    console.log("test")
})

基本的にuseEffectはレンダリング時に毎回実行されます。第二引数に変数を渡すことで以下のように発火タイミングを制御できます。

初回マウント時とvalueの値が変動した場合のみ発火される

"use client"
import { useEffect } from "react";
const Home = () => {
    let value = ""
    //useEffect
    useEffect(() => {
        //発火させる処理
        console.log(value)
    //第二引数にvalueを渡す
    },[value])
}
export default Home

初回マウント時のみ発火される([]は変動することが無いため初回の1度だけ)

"use client"
import { useEffect } from "react";
const Home = () => {
    let value = ""
    //useEffect
    useEffect(() => {
        //発火させる処理
        console.log(value)
    //第二引数に[]を渡す
    },[])
}
export default Home

useContext

コンテキストを作成しラップする事で配下のコンポーネントツリー全体で値が共有できるようになる。
※ネスト構造のコンポーネントのバケツリレー方式のpropsの受け渡しをしなくて済む

Root > Parent > Children のような構造で Root → Children にデータや値を渡したい時などにParentを経由しなくて済むため便利なhooks

-使用例-

export const Context = createContext(初期値)
export const Context = useContext(作成したContext)

createContextを使ってContextを作成し、Context.Providerでラップします。
ラッピング配下でuseContextを使用し、引数に作成したContextを取り値を受け取ることが出来ます。

Rootコンポーネント(一番上の階層)

Root.tsx
"use client"
import { createContext, useState, } from "react";
import Parent from "./components/Parent";
interface ContextType {
  value: number,
  setValue: (index: number | any) => void
}
export const Context = createContext<ContextType>({value: 0, setValue: () => {}})
const Home = () => {
  const [value, setValue] = useState(0)
  return(
    <>
    <Context.Provider value={{value, setValue}}>
      <Parent />
      <div>値: {value}</div>
    </Context.Provider>
    </>
  )
}
export default Home

Childrenコンポーネント(一番下の階層)

Children.tsx
import React, { useContext } from 'react'
import { Context } from '../page'

const Children = () => {
  //useContextで受け取る
  const {value, setValue} = useContext(Context)
  return (
    <>
      <div>Parent</div>
      <button onClick={() => setValue((prev: number) => prev + 1)}>ボタン</button>
      <div>
        値: {value}
      </div>
    </>
  )
}

export default Children

上記の処理はuseStateの時と同様にボタンを押すと1加算される処理になってます。useContextを使用で共通のvalueを受け渡せているため、Rootコンポーネント/Childコンポーネントどちらぼボタンを押しても同じ値に変動し表示されているはずです。

おわりに

今回は使用頻度が高いhookを3つだけ紹介しましたが、残りのhooksたちも今後記事に残そうと思います。もしここの説明全然違うやん..などがあれば是非指摘して頂いたら幸いです。

最後まで読んで頂き有難うございました

Discussion