プログラミング

Reactでグローバルstateを扱うためのuseContextを紹介【簡単】

reactのusecontextを解説

今回はReactの機能の1つである、useContextについての解説を行います。

このuseContextを利用することで、様々なコンポーネントから参照できるstateを作ることができます。

つまり、無駄なpropsのバケツリレーなどをしなくて良くなるということです。

グローバルstateは他にもReduxやRecoilなどがありますが、このuseContextが基礎となりますので、しっかり身につけましょう。

今回は、前回のこの記事の続きから実装していきます。

【簡単】Reactのルーティングを紹介【useHistoryの使い方】Reactのルーティングについて理解してますか?React routerを使えば簡単にルーティングを実装できます。なので今回は、Reactのルーティングについて解説します。useHistoryの使い方も合わせて解説しているので、参考になるかと。...

Reactエンジニアを目指している方へ、

知識0からReactエンジニアになるまでのロードマップをまとめました。
こちらも記事もぜひ参考にどうぞ。

Reactロードマップ
【完全無料】Reactエンジニアへなるための学習ロードマップ【高年収】Reactを学びたいと思ってますか?Reactエンジニアから勉強方法を学ぶのが一番効率的です。なので今回は、現役のReactエンジニアがReactの学習ロードマップを無料で公開します。Reactは今とても需要が高いスキルなので、今すぐ学びましょう。...

スクールでReactを深く学びたい方へ、

Reactを学べるおすすめのプログラミングスクールもまとめてますので、こちらもどうぞ。

reactを学べるプログラミングスクール
【最新】Reactを学べるおすすめプログラミングスクール3選Reactを学びたいですか?プログラミング学習で最も効率的なのはプログラミングスクールに通うことです。なので今回は、おすすめのプログラミングスクールを厳選しました。これでスクール選びで失敗することは無いです。Reactは需要が高いスキルなので、今すぐ学びましょう。...

Reactのおすすめ教材が知りたい方へ、

現役のReactエンジニアが>Reactのおすすめ教材をまとめました。
こちらも記事もぜひ参考にどうぞ。

【入門】React初心者が0から知識を身につける勉強法・教材のまとめ「React初心者だけど、勉強して入門したい」と思っている方必見。記事では、React初心者に向けて入門レベルからおすすめの学習教材を紹介しています。Reactエンジニアである僕が、勉強法なども合わせて解説しているのかなり参考になるかと。...

\技術で詰まった時に質問し放題/

Reactでの基本的なグローバルStateの設定

まずは、「グローバルstate」の作り方と、その参照に仕方を説明していきます。

今回ユーザーの情報を「グローバルstate」として設定するので、「UserProvider」というファイルを作成します。

次に、「userInfo」という「state」を宣言します。

中身は、「userName」を設定しておきます。

そして、「createContext」というものを使って、「グローバルstate」を設定していきます。

次に、「UserProvider」というコンポーネントを作成し、「props」の「children」を表示させるようにします。

ここは、もう書き方を覚えてしまうのが一番良いので、コードはこんな感じになります。

import { createContext, useState } from "react";

export const UserContext = createContext({});

export const UserProvider = (props) => {
  const [userInfo, setUserInfo] = useState({userName: "shinya"});
  return(
    <UserContext.Provider value={{userInfo, setUserInfo}}>
      {props.children}
    </UserContext.Provider>
  )
}

これで、「userInfo」という「グローバルstate」を設定することができました。

次に、Home側でそれを参照できるようにします。

まず、どこからでも「グローバルstate」を呼び出せるように、先ほど作成した「UserProvider」で全てのコンポーネントを囲みます。

import './App.css';
import {BrowserRouter, Link} from "react-router-dom";
import {Routes} from "./components/routing/Routes"
import { UserProvider } from './providers/UserProvider';

function App() {
  return (
    <UserProvider>
      <BrowserRouter>
        <div className="App">
          <Link to="/">Home</Link>
          <br />
          <Link to="/page1">Page1</Link>
          <br />
          <Link to="/page2">Page2</Link>
          <br />
          <Link to="/countUp">CountUp</Link>
        </div>
        <Routes />
      </BrowserRouter>
    </UserProvider>
  );
}

export default App;

参照する側は、「useContext」というものを使います。

引数に、先ほど作成した「UserContext」を設定すればOKです。

後は普通にこのように書けば先ほど設定した、「userName」が表示されるかと思います。

import {useContext} from "react";
import {UserContext} from "../../providers/UserProvider";

export const Home = () => {
  const {userInfo} = useContext(UserContext);
  return(
    <>
      <h2>Home</h2>
      <p>{userInfo.userName}</p>
    </>
  );
}

グローバルstateの変更

では次に、「グローバルstate」を変更させ、全てのコンポーネントでその変更が反映されているかを確認します。

今回は、「Page1」で「useInfo」を変更させます。

まずは、変更用にinputエリアとボタンを用意します。
ここに設定した値が新しい「userName」として、変更させるように関数を設定します。

コードはこんな感じです。

import {Link, useHistory} from "react-router-dom";
import {useContext, useState} from "react";
import {UserContext} from "../../providers/UserProvider";

export const Page1 = () => {
  const stateA = "state";
  const history = useHistory();
  const [inputUserName, setInputUserName] = useState("");
  const {userInfo, setUserInfo} = useContext(UserContext)
  const onClickButton = () => {
    //history.push("/page1/detailA")
    history.push({
      pathname: "/page1/detailA",
      state: stateA
    })
  }
  const onChangeInput = (e) => {
    setInputUserName(e.target.value);
  }
  const changeUserName = () => {
    if(inputUserName === ""){
      return
    }
    setUserInfo({userName: inputUserName});
    setInputUserName("");
  }
  return(
    <>
      <Link to={{pathname: "/page1/detailA", state: stateA}}>DetailA</Link>
      <br />
      <Link to="/page1/detailB">DetailB</Link>
      <h2>Page1</h2>
      <br />
      <button onClick={onClickButton}>Jump to DetailA</button>
      <br />
      <p>{userInfo.userName}</p>
      <input type="text" value={inputUserName} onChange={onChangeInput}/>
      <button onClick={changeUserName}>change user name</button>
    </>
  );
}

これで、インプットを指定してボタンを押すと「userName」が変更になりましたが、本当に他のコンポーネントにも反映されているのでしょうか?

Homeを確認すると分かりますが、ちゃんと他のページにも反映されています。

Reactでグローバルstateを使う際の注意点

最後に、「グローバルstate」を使う際の注意点を書いていきます。

「グローバルstate」が変更になると、その「state」を使っている全てのコンポーネントの再レンダリングが走ります。

そこまでは良いのですが、stateを参照していない無関係の子コンポーネントまで変更されることになってしまいます。

なぜなら、Reactの再レンダリングの条件は
・stateが変更になった時
・propsが変更になった時
・親コンポーネントが再レンダリングされた時
なので、最後の条件に当てはまってます。

試しに、Page1コンポーネントの子コンポーネントを作り、そこにログを仕込みます。

import {Link, useHistory} from "react-router-dom";
import {useContext, useState, memo} from "react";
import {UserContext} from "../../providers/UserProvider";

const Page1Child = () => {
  console.log("child")
  return(
    <h2>Page1Child</h2>
  )
};

export const Page1 = () => {
  const stateA = "state";
  const history = useHistory();
  const [inputUserName, setInputUserName] = useState("");
  const {userInfo, setUserInfo} = useContext(UserContext)
  const onClickButton = () => {
    //history.push("/page1/detailA")
    history.push({
      pathname: "/page1/detailA",
      state: stateA
    })
  }
  const onChangeInput = (e) => {
    setInputUserName(e.target.value);
  }
  const changeUserName = () => {
    if(inputUserName === ""){
      return
    }
    setUserInfo({userName: inputUserName});
    setInputUserName("");
  }
  return(
    <>
      <Link to={{pathname: "/page1/detailA", state: stateA}}>DetailA</Link>
      <br />
      <Link to="/page1/detailB">DetailB</Link>
      <h2>Page1</h2>
      <br />
      <button onClick={onClickButton}>Jump to DetailA</button>
      <br />
      <p>{userInfo.userName}</p>
      <input type="text" value={inputUserName} onChange={onChangeInput}/>
      <button onClick={changeUserName}>change user name</button>
      <Page1Child />
    </>
  );
}

グローバルstateが変更される度にその子コンポーネントも再レンダリングされていることがわかるかと思います。

つまり、きちんと再レンダリングの最適化をしていないと無駄な再レンダリングを走らせることになります。

対策は簡単で、単にmemo化してあげれば良いだけです。
コードはこうなります。

import {Link, useHistory} from "react-router-dom";
import {useContext, useState, memo} from "react";
import {UserContext} from "../../providers/UserProvider";

const Page1Child = memo(() => {
  console.log("child")
  return(
    <h2>Page1Child</h2>
  )
});

export const Page1 = () => {
  const stateA = "state";
  const history = useHistory();
  const [inputUserName, setInputUserName] = useState("");
  const {userInfo, setUserInfo} = useContext(UserContext)
  const onClickButton = () => {
    //history.push("/page1/detailA")
    history.push({
      pathname: "/page1/detailA",
      state: stateA
    })
  }
  const onChangeInput = (e) => {
    setInputUserName(e.target.value);
  }
  const changeUserName = () => {
    if(inputUserName === ""){
      return
    }
    setUserInfo({userName: inputUserName});
    setInputUserName("");
  }
  return(
    <>
      <Link to={{pathname: "/page1/detailA", state: stateA}}>DetailA</Link>
      <br />
      <Link to="/page1/detailB">DetailB</Link>
      <h2>Page1</h2>
      <br />
      <button onClick={onClickButton}>Jump to DetailA</button>
      <br />
      <p>{userInfo.userName}</p>
      <input type="text" value={inputUserName} onChange={onChangeInput}/>
      <button onClick={changeUserName}>change user name</button>
      <Page1Child />
    </>
  );
}

これで無駄な再レンダリングが走らなくなったかと思います。

Reactでグローバルstateを扱うためのuseContextを紹介 まとめ

このようにすることで、「グローバルstate」を扱うことができ、無駄なpropsのバケツリレーを無くすことができます。

Reactにおいて、状態管理はとても大事な概念なので、今回学んだことはしっかりと扱えるように復習しておきましょう。

僕のブログでは他にもプログラミングについての記事を書いているので、良かったら見てみてください。

Reactエンジニアになりたい人はこちらからどうぞ

Reactロードマップ
【完全無料】Reactエンジニアへなるための学習ロードマップ【高年収】Reactを学びたいと思ってますか?Reactエンジニアから勉強方法を学ぶのが一番効率的です。なので今回は、現役のReactエンジニアがReactの学習ロードマップを無料で公開します。Reactは今とても需要が高いスキルなので、今すぐ学びましょう。...

スクールでReactを深く学びたい人はこの記事をどうぞ。

reactを学べるプログラミングスクール
【最新】Reactを学べるおすすめプログラミングスクール3選Reactを学びたいですか?プログラミング学習で最も効率的なのはプログラミングスクールに通うことです。なので今回は、おすすめのプログラミングスクールを厳選しました。これでスクール選びで失敗することは無いです。Reactは需要が高いスキルなので、今すぐ学びましょう。...

Reactのおすすめ教材を知りたい人はこの記事をどうぞ。

【入門】React初心者が0から知識を身につける勉強法・教材のまとめ「React初心者だけど、勉強して入門したい」と思っている方必見。記事では、React初心者に向けて入門レベルからおすすめの学習教材を紹介しています。Reactエンジニアである僕が、勉強法なども合わせて解説しているのかなり参考になるかと。...
ABOUT ME
ひのしん
【執筆者情報】ひのしんです。「正しい努力をすれば誰でも成果は出せる」 をモットーに発信しています。元ニートのフリーランスエンジニア。 年収240万→840万,TOEIC425→885。0スキルアップに関する知識を発信してます。分かりやすさ、効率の良さ、簡単さを意識して記事を書いています。
【次世代型】サブスクのプログラミングスクール

「プログラミングスクールが高くて通えない。。」

といった悩みがこのサービスで解決します。

それは、次世代型のサブスクプログラミングスクールになります。

具体的に、このスクールは以下のことが可能です。

  • 講師とのマンツーマンレッスン
  • 質の高いかなりボリュームのある教材
  • 講師に質問し放題

そして、お値段はたったの1,980円から。

これで高いお金を払わずに、エンジニアになることが可能です。

今なら、全額返金保証もあります。

エンジニアを目指す人も年々増えているで、お早めにどうぞ。

当サイト限定の、初月50%OFFクーポン(HINOSHIN)あり

>>侍テラコヤの評判・口コミ|現役エンジニアが実際に使ってみた感想

\全額返金保証/
侍テラコヤを詳しく見る