3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【React】useReducerとuseContext

Posted at

useReducer

useStateと同様、状態管理に使用するもう一つのフック。


// useStateの場合
const [ state, setState ] = useState(0);

// useReducerの場合
const [ rState, dispatch ] = useReducer( prev => ++prev, 0);

・配列の0番目
状態管理(ステート)の変数。

・配列の1番目
更新用関数。useStateと違い、配列の1番目の更新用関数名はdispatchと記載することが一般的。
(useStateのように任意の関数名でも動く。)

dispatchを実行すると、useReducerの引数で記載したprev => ++prevが実行される。



ポイント

・useStateの場合 → 更新用関数の中で、ステートをど更新するか定義する。

・useReducerの場合 → useReducerの定義で、引数内でステートをどう更新するかを定義する。

useReducerの特徴

useStateと違い、複雑な処理を行なって更新するのに向いている。


import { useReducer, useState } from "react";

const Example = () => {

  const [ state, setState ] = useState(0);

  const [ rState, dispatch ] = useReducer(
    (prev, action) => {
      if(action === '+') {
        return ++prev ;
      }else {
        return --prev;
      }
    }, 0);

  return (
    <>
      <h3>{state}</h3>
      <button onClick={() => setState(prev => prev + 1)}>+</button>
      <h3>{rState}</h3>
      <button onClick={() => dispatch('+') }>+</button>
      <button onClick={() => dispatch('-') }>-</button>
    </>
  );
};

export default Example;

以下のように、関数を分けて定義し、useReducerの第一引数に渡してもOK.

import { useReducer } from "react";

const Example = () => {

  const reducer = (prev, action) => {
      if(action === '+') {
        return ++prev ;
      }else {
        return --prev;
      }
  }


  const [ rState, dispatch ] = useReducer(reducer, 0);

  return (
    <>
      <h3>{state}</h3>
      <button onClick={() => setState(prev => prev + 1)}>+</button>
      <h3>{rState}</h3>
      <button onClick={() => dispatch('+') }>+</button>
      <button onClick={() => dispatch('-') }>-</button>
    </>
  );
};

export default Example;



reducer関数の定義

reducer関数とは、dispatchによって実行される更新関数のこと。上記の例でいうredcuerメソッドがreducer関数に該当する。

このreducer関数には、下記のように記述することが基本である。


    // reducer関数の定義
    // state → 現状のステート
    // action → dispatchから受け取った、コンポーネントで実行された操作(データ)
    const reducerFunction = (state, action) => {

    // ステートを更新するための処理

      return newState;        
    };

dispatchとは...?

dispatchとは、引数にactionという、コンポーネントによって実行された操作を受け取る。

→ actionというオブジェクトを引数に代入し、reducer関数に渡す。
第二引数はないので、複数渡すことはできない。

dispatch(action, value) // エラー

主に

{type: 捜査の種類, payload: 操作によって取得また更新されるデータ}

のように、typeプロパティ('action'の識別子)とpayloadプロパティ(データ)で構成されることが多い。

基本的には、

①dispatchでreducer関数に対してactionを渡す。

②reducer関数はactionのtypeやpayloadの状況を利用してstateの更新を行う。

という形で利用するのが定石。



reducer関数とは...?

reducer関数は、stateとactionを引数に受け取り、stateを更新する関数のこと。

注意!!

reducer関数はstateactionという2つの引数を受け取ることになっているが、
dispatchは必ずactionという値(データ)のみを引数に取るので、redcuerが2つだからdispatchも2つという認識は誤り。


useContext

useContextは、アプリケーション全体(グローバル)でステートを管理したいときに使用する。

どういうときに使用するか

useContextがない場合、

// Example.jsx

import Child from "./components/Child";

const Example = () => {
  const value = 'hello'
  return <Child value={value}/>;
};

export default Example;


// Child.jsx
import GrandChild from "./GrandChild";

const Child = ({ value }) => (
  <div style={{ border: "1px solid black", padding: 10 }}>
    <h3>子コンポーネント</h3>
    <GrandChild />
  </div>
);

export default Child;


// GrandChild.jsx

const GrandChild = ({ value }) => {

  return (
      <div style={{ border: "1px solid black" }}>
        <h3>孫コンポーネント</h3>
        {value}
      </div>
  );
};
export default GrandChild;

Example.jsxから、子コンポーネントのGrandChild.jsxにpropsを渡したい場合、その間にあるChild.jsxに一度propsを渡すという工程が必要になる。

これをReactでは、Propsのバケツリレーと呼び、propsの悪い渡し方の例となっている。
Childでは親のExample.jsxから渡されたpropsは使用しないため、不要なコードにもなる。

そのような場面で、ステートをグローバルに管理しておくことで、どの階層のコンポーネントにもpropsを一発で渡すことができる、useCotnextが便利である。

書き方

Example.jsx

import Child from "./components/Child";
// createContextをインポート
import { createContext } from "react";

// createContextでグローバルに管理するコンテキストを作成。
export const MyContext = createContext("helio");

const Example = () => {
  const value = 'hello'
  return <Child />;
};

export default Example;

GrandChild.jsx

// 親コンポーネントで作成したコンテキストをインポート
import { MyContext } from "../Example";
// useContextをインポート
import { useContext } from "react";

const GrandChild = () => {
  // useContextを定義。このとき、引数にコンテキストを代入する。
  const value = useContext(MyContext);

  return (
      <div style={{ border: "1px solid black" }}>
        <h3>孫コンポーネント</h3>
        {value}
      </div>
  );
};
export default GrandChild;

useContextとuseState

useContextを用いて、usaeStateをグローバルに管理できるようにする方法は以下の通り。

Example.jsx

import { createContext } from "react";
import { useState } from "react";

// コンテキストの新規作成
export const MyContext = createContext(0);

const Example = () => {
  // ステートの定義
  const [ value, setValue ] = useState(0);

  return (
    <>
    // 作成したコンテキスト名.Provider というコンポーネントを作成。
    // そのコンポーネントのpropsに、グローバル管理をしたい対象のステートを代入する。
    <MyContext.Provider value={[ value, setValue ]}>

        // コンテキストのコンポーネントタグに囲まれたコンポーネント内で、コンテキストの参照が可能になる。
      <Child />
      <OtherChild />
    </MyContext.Provider>
    </>
  );
};

export default Example;

OtherChild.jsx

import { useContext } from "react";
import { MyContext } from "../Example";

const OtherChild = () => {
  // 配列の2番目のみを取得したい場合、1番目を何も入力せず、[, ]とする。
  const [, setValue ] = useContext(MyContext);
  
  const clickHandler = (e) => {
    setValue((prev) => prev + 1);
  };

  return (
    <div style={{ border: "1px solid black" }}>
      <h3>他の子コンポーネント</h3>
      <button onClick={clickHandler}>+</button>
    </div>
  );
};

export default OtherChild;

Child.jsx

import GrandChild from "./GrandChild";

const Child = () => {
  return (
    <div style={{ border: "1px solid black", padding: 10 }}>
      <h3>子コンポーネント</h3>
      <GrandChild />
    </div>
  )
};
export default Child;

GrandChild.jsx

import { useContext } from "react";
import { MyContext } from "../Example";
const GrandChild = () => {
  // GrandChildはExample.jsxのコンポーネントに囲まれていなかったが、
  // 親コンポーネントであるChild.jsxがコンポーネントで囲まれていたので、GrandChildでも取得が可能となっている。
  const [ value ] = useContext(MyContext);
  return (
    <div style={{ border: "1px solid black" }}>
      <h3>孫コンポーネント</h3>
      {value}
    </div>
  );
};
export default GrandChild;

useContextの注意点

useContextでは、ステートを更新した際、useContextを参照しているコンポーネントが全て再レンダリングされる。

これは、例え参照している値がステートの変数等ではなくても(更新用の関数などでも)、useContextを参照しているというだけで再レンダリングの対象になる。

参考文献

3
3
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
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?