【React 入門】グローバルなstateの管理方法

React

今回は、どのコンポーネントからでもデータの参照や更新をするためのグローバルなstateの管理方法を見ていきたいと思います。

Contextを用いた管理

基本的な使い方

今回は、以下のようなログイン・ログアウトを実装します。
(ログイン情報の入力画面は省略)


この例では、以下のユーザー情報をグローバルに管理したいと思います。
・名前
・メールアドレス
・ログイン状況


まず、以下のようなプロバイダーを作成します。

import React, { useState, createContext } from 'react'

export const UserContext = createContext({})

export const UserProvider = (props) => {
    const {children} = props;
    const [userInfo, setUserInfo] = useState({ name: '山田太郎', email: 'yamada@sample.com', isAdmin: false })

    return (
        <UserContext.Provider value={{ userInfo, setUserInfo }}>
            {children}
        </UserContext.Provider>
    )
}


以下のように、createContext({}) でContextを作成します。

export const UserContext = createContext({})


次に、useState で初期値を設定しています。

const [userInfo, setUserInfo] = useState({ name: '山田太郎', email: 'yamada@sample.com', isAdmin: false })


そして、JSXの中で以下のような形式で記述します。

<Context名.Provider value={{ グローバルに管理したいデータ }}>
    {children}
</Context名.Provider>



定義したプロバイダー(UserProvider)を使用するには、
このグローバルなstateを使用したいコンポーネント全体をプロバイダータグで囲みます。

import React from 'react'
import { Main } from './pages/Main';
import { UserProvider } from "./providers/UserProvider";

function App() {
    return (
        <UserProvider>
            <Main />
        </UserProvider>
    );
}

export default App;


これにより、Mainコンポーネント配下で 「userInfo, setUserInfo」を参照・呼び出しすることができます。


続けて、残りのコンポーネントを書いておきます。
(スタイリングにはBootstrapを使用しています)

import React, { useContext } from 'react'
import { User } from '../components/User'
import { UserContext } from '../providers/UserProvider'

export const Main = () => {
    const { userInfo, setUserInfo } = useContext(UserContext)
    const isAdmin = userInfo ? userInfo.isAdmin : false
    const onClickSwitch = () => setUserInfo({ name: userInfo.name, email: userInfo.email, isAdmin: !userInfo.isAdmin });

    return (
        <>
            <header className="p-3 shadow-sm d-flex justify-content-end align-items-center">
                {isAdmin && userInfo.name}
                {isAdmin || 'ゲスト'}
                さん
                <button className="ml-2 btn text-primary" onClick={onClickSwitch}>
                    {isAdmin && 'ログアウト'}
                    {isAdmin || 'ログイン'}
                </button>
            </header>
            <div className="container mt-4">
                <User />
            </div>
        </>
    );
}
import { UserCardHeader } from "./UserCardHeader"
import { UserCardBody } from "./UserCardBody"

export const User = () => {
    return (
        <div className="card">
            <UserCardHeader />
            <UserCardBody />
        </div>
    )
}
import React, { useContext } from 'react'
import { UserContext } from '../providers/UserProvider'

export const UserCardHeader = () => {
    const { userInfo } = useContext(UserContext)
    return (
        <div className="card-header d-flex justify-content-between align-items-center">
            プロフィール
            {userInfo.isAdmin &&
            <button className="btn btn-outline-secondary">編集</button>
            }
        </div>
    )
}
import React, { useContext } from 'react'
import { UserContext } from '../providers/UserProvider'

export const UserCardHeader = () => {
    const { userInfo } = useContext(UserContext)
    return (
        <div className="card-header d-flex justify-content-between align-items-center">
            プロフィール
            {userInfo.isAdmin &&
            <button className="btn btn-outline-secondary">編集</button>
            }
        </div>
    )
}



実際にコンポーネントでContextのstateを使用するには、以下の形式で取り出すことができます。

const { プロパティ名 } = useContext(Context名)

再レンダリングの最適化

今回の例でログイン・ログアウトを変更したとき、
Main コンポーネントが再レンダリングされると、その子コンポーネントも再レンダリングが起きてしまいます。

User コンポーネントは再レンダリングする必要がないため、
memo() で囲むことでContext や props に変更がない限り再レンダリングが起きないようにすることができます。

import { UserCardHeader } from "./UserCardHeader"
import { UserCardBody } from "./UserCardBody"
import React, { memo } from "react"

export const User = memo(() => {
    return (
        <div className="card">
            <UserCardHeader />
            <UserCardBody />
        </div>
    )
})




今回は以上になります。
ご覧いただきありがとうございました!

コメント

コンタクトフォーム

    タイトルとURLをコピーしました