HOME  >  Web開発 >  【React.js】useContextとuseReducerの使い方をご紹介(React hooksを学ぶ)

Web開発

【React.js】useContextとuseReducerの使い方をご紹介(React hooksを学ぶ)

【React.js】useContextとuseReducerの使い方をご紹介(React hooksを学ぶ)

目次

  1. useContextについて
  2. useReducerについて
  3. useContextとuseReducerを合わせて使う
  4. 最後に

今回は、前回に引き続きReact hooksについて記事をまとめていきます。

前回は、useStateとuseEffectの使い方を紹介しました。

【React.js】useStateとuseEffectの使い方をご紹介(React hooksを学ぶ)
https://www.dailyupblog.com/web_development/1888/

今回は、Context機能を利用できるフックであるuseContext、useState同様状態管理のフックであるuseReducerの2つのフックをご紹介します。

それでは早速いきましょう!!

useContextについて

useContextは、ReactのContext機能を利用できるフックです。

Context機能とは、Reactコンポーネントツリー上でデータを簡単にやり取りできるようにしてくれる機能です。

コンポーネント間でデータをやり取りする時はpropsを使いますが、コンポーネントの階層が複数になるとバケツリレーでデータを渡すことになるので、コードが複雑でわかりにくくなってしまいます。

その問題を解決すべく、コンポーネント間で簡単にデータアクセスできるようにしたのがContextです。

下図のように、Contextでは階層を飛ばして、データにアクセスすることが可能になります。

早速このuseContextでContext機能を使ってみましょう。

「components」フォルダ内に三つのコンポーネントファイルを作成します。

・Context1.tsx

import Context2 from './Context2';

const Context1 = () => <Context2 />

export default Context1

・Context2.tsx

import Context3 from './Context3';

const Context2 = () => <Context3 />

export default Context2

・Context3.tsx

import { useContext } from 'react';
import { UserDataContext } from '../App';

const Context3 = () => {
	const userData = useContext(UserDataContext);
	return (
		<p>名前:{userData.name}<br />年齢:{userData.age}<br />職業:{userData.works}</p>
	)
}

export default Context3

Context1.tsxではContext2.tsxを、Context2.tsxではContext3.tsxを呼び出して階層にしています。

そして、Context3.tsxでは、useContextを使用して、渡ってきたデータを出力しています。

今回は、App.tsxファイルのUserDataContextというユーザーのオブジェクトデータにアクセスして、そのデータを使用します。

さて、App.tsxも編集していきます。

「src」直下のApp.tsxの内容を下記のようにしてください。

import { createContext, useState } from 'react';
import './App.css';
import Context from './components/Context1';

type userData = {
  name: string,
  age: number,
  works: string
}

export const UserDataContext = createContext<userData>({name: "",age: 0, works: ""})

function App() {
  const [userData, setUserData] = useState<userData>({
    name: "太郎",
    age: 26,
    works: "会社員"
  })
  return (
    <div>
      <UserDataContext.Provider value={userData}>
        <Context />
      </UserDataContext.Provider>
    </div>
  );
}

export default App;

ContextをはcreateContextで作成することができます。

createContextの構文は下記の通りです。

createContext(初期値)

今回は、typeScriptを使用しているので、userDataというtypeAriasを宣言しており、createContextにはgenericsでこのuserData型を指定しています。

このApp.tsxでは、Context1.tsxをインポートして呼び出していますが、createContextの現在値は、ツリー内でこのフックを呼んだコンポーネントの直近に存在する、Context.Providerのvalue属性の値によって決まります。

今回は、userDataをuseStateで作成しており、この値をvalueに入れています。

なので、このuserDataの値が変化することにより、Contextも変化するということです。

さて、今回の例を画面で実行してみましょう。

App.tsxを同階層のindex.tsxで読み込むようにしてください。

Reactを起動して、ブラウザで「http://localhost:3000」にアクセスしてみてください。

下記のようにユーザー情報が表示されていれば、OKです。

useReducerについて

useReducerは、useState同様、値を保持したり更新したりできる状態管理のフックです。

ここでは、useStateとはどのように違うのか、使い方の例を挙げて紹介していきます。

useReducterの大きな特徴としては、stateにactionというstateに関連する処理を加えてnewStateを作成することです。

このnewStateをreducerとして設定し、stateに更新を加える処理をactionで受け取った値によって出しわけて書くことが可能です。

と説明してもわかりにくいと思いますので、useReducerを使った例を見てみましょう。

ちなみに、useReducerの基本構文か下記になります。

const [state, dispatch] = useReducer(reducer, '初期値');

ここでの、「reducer」は、stateを更新するための関数になります。

また、「dispatch」は、reducerを実行するための関数になります。

変数宣言の際にstateの更新方法を先に宣言しておくことが可能です。

さて、簡単なカウンターを作ってみます。

「components」直下にCounter.tsxを作成し、その中に下記のように記述してください。

import { useReducer } from 'react';

type State = { count: number };
const initialState: State = { count: 0 };

type Action =
  {
    type: "INCREMENT",
  } |
  {
    type: "DECREMENT",
  } |
  {
    type: "RESET"
  };

const reducer = (state: State, action: Action): State => {
	switch(action.type) {
		case 'INCREMENT':
			return { count: state.count + 1 };
		case 'DECREMENT':
			return {count: state.count - 1};
		case 'RESET':
			return {count: 0};
	}
}

const Counter = () => {
	const [state, dispatch] = useReducer(reducer, initialState);
	return (
		<div>
			<p>カウント:{state.count}</p>
			<button onClick={() => dispatch({ type: "INCREMENT"})}>➕</button>
			<button onClick={() => dispatch({ type: "DECREMENT"})}>➖</button>
		</div>
	)
}

export default Counter;

一つずつ見てみます。

まず下記でreducerの初期値を設定しておきます。

初期値である「initialState」は0であり、number型のcountプロパティをもつState型を宣言しており、この型を設定しています。

type State = { count: number };
const initialState: State = { count: 0 };

下記では、のちに設定するreducer関数のaction引数の型を宣言しています。

このaction引数はユニオンタイプによって、この3つどれかの値しか持ちません。

type Action =
  {
    type: "INCREMENT",
  } |
  {
    type: "DECREMENT",
  } |
  {
    type: "RESET"
  };

下記がreducer関数です。

ここでは、のちにuseReducerを記述する際にあらかじめstateの更新内容を設定しておくことができます。

今回は、action引数の内容によって、stateに1を足すのか1を引くのか、はたまた0に戻すのかをswitch文で出しわけて書いています。

const reducer = (state: State, action: Action): State => {
	switch(action.type) {
		case 'INCREMENT':
			return { count: state.count + 1 };
		case 'DECREMENT':
			return {count: state.count - 1};
		case 'RESET':
			return {count: 0};
	}
}

下記はCounterの関数コンポーネントになります。

この中でuseReducerを使用しています。

useReducerの第一引数には先ほど設定しておいたreducer関数を、第二引数には初期値としてinitialStateを入れています。

そして、htmlの中では、buttonタグにonClickでクリックイベントを設定していますが、そこにdispatchを入れており、引数に「INCREMENT」と「DECREMENT」をそれぞれ入れて、クリックした際に数値が足されるか引かれるかを記述しています。

const Counter = () => {
	const [state, dispatch] = useReducer(reducer, initialState);
	return (
		<div>
			<p>カウント:{state.count}</p>
			<button onClick={() => dispatch({ type: "INCREMENT"})}>➕</button>
			<button onClick={() => dispatch({ type: "DECREMENT"})}>➖</button>
		</div>
	)
}

さて、ここまで書いたら、早速ブラウザで挙動を確認してみましょう。

index.tsxでは、今回作ったCounterコンポーネントが呼び出されるように書き換えてください。

ブラウザで「http://localhost:3000」にアクセスしたら、下記のように表示されます。

プラスボタンとマイナスボタンをクリックして、数値の表示が変化したらOKです。

useContextとuseReducerを合わせて使う

useContextとuseReducerは一緒に使うことが可能です。

先ほどのuseContextの例で、状態管理のuseStateを一緒に使っていましたが、useStateの代わりにuseReducerを使います。

カウンターの例を作ってみます。

先ほどと同じようにContext1.tsx、Context2.tsx、Context3.tsxの三つのコンポーネントを作成します。

Context1.tsxとContext2.tsxの内容は先ほどと同様なので省きます。

Context3.tsxは下記のように記述してください。

import { useContext } from 'react';
import { CountContext } from '../App';

const Context3 = () => {
	const {state, dispatch} = useContext(CountContext);
	return (
		<div>
					<p>{state.count}</p>
					<button onClick={() => dispatch({type: "INCREMENT"})}>+</button>
					<button onClick={() => dispatch({type: "DECREMENT"})}>−</button>
		</div>
	)
}

export default Context3

useContextでuseReducerのstateとdispatchを受け取ります。

そして、dispatch関数をそれぞれボタンのクリックイベントにセットします。

引数には、useReducerのreducer関数の第二引数であるactionの値を入れます。

プラスボタンには「{type: “INCREMENT”}」、マイナスボタンには「{type: “DECREMENT”}」を入れます。

続いて、App.tsxを下記のように書き換えてください。

import React, { useReducer } from 'react';
import './App.css';
import Context from './components/Context1';

type State = { count: number }

type Action = {
  type: "INCREMENT"
} | {
  type: "DECREMENT"
} | {
  type: "RESET"
}

type ContextType = {
  state: { count: number };
  dispatch: (value: Action) => void;
}

export const CountContext = React.createContext<ContextType>({} as ContextType);

const initialState: State = { count: 0 }

const reducer = (state: State, action: Action): State => {
  switch(action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return {count: state.count - 1};
    case 'RESET':
      return {count: 0};
  }
}

function App() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <div>
      <CountContext.Provider value={{state, dispatch}}>
        <Context />
      </CountContext.Provider>
    </div>
  );
}

export default App;

まず、いくつかType Aliasを宣言しておきます。

「state」と「Action」はそれぞれuseReducerのreducer関数の引数で使う型になります。

他に「ContextType」という型を宣言していますが、これはcreateContextの引数で指定する型です。

createContextの引数に入れる初期値は、その後のContext.Providerのvalue属性の値と同一の型でなければなりません。

その後の流れは、reducer関数でstateの処理の出しわけを書き、useReducerのstateとdispatchをContext.Providerのvalue属性の値に設定しています。

これで、useReducerの値と関数をuseContextにて別コンポーネントで利用できるようになるわけです。

ブラウザで下記のように表示され、カウンターが機能すればOKです。

最後に

今回は、react hooksのuseContextとuseReducerの使い方をご紹介しました。

どちらもとても便利で、頻出も多そうです。

前回の2つ同様に使いこなせるようになりたいところです。

次回も同じようにreact hooksを紹介していくので、是非ご覧ください!

前回:【React.js】useStateとuseEffectの使い方をご紹介(React hooksを学ぶ)
https://www.dailyupblog.com/web_development/1888/

次回:【React.js】useCallback、useMemoの使い方をご紹介(React hooksを学ぶ)
https://www.dailyupblog.com/web_development/1956/

関連記事
  • 【React.js】React Routerで画面遷移をして……

  • 【React.js】React Routerで画面遷移をして

    【React.js】useCallbackとuseMemoの……

  • 【React.js】useCallbackとuseMemoの

    【React.js】React.jsとは?環境構築やHell……

  • 【React.js】React.jsとは?環境構築やHell

    【React.js】useLayoutEffectとuseD……

  • 【React.js】useLayoutEffectとuseD

    【React.js】React.jsにおけるコンポーネントと……

  • 【React.js】React.jsにおけるコンポーネントと

    【React.js】useStateとuseEffectの使……

  • 【React.js】useStateとuseEffectの使

    【React.js】useRefとuseImperative……

  • 【React.js】useRefとuseImperative

    【React.js】useTransitionとuseIdの……

  • 【React.js】useTransitionとuseIdの

    【React.js】CSS-in-JSを使ったスタイリング(……

関連記事