[React] Global State 관리

👉 React State

👉 local state

React에서의 local state는 props와 state로 나뉜다.
props는 상속받은 상태에 대한 관리이고, state는 component가 생성한 상태에 대한 관리이다.
✏️ component 상태 관리

👉 global state

component에서 다른 component로 state를 전달해주기 위해서는 반복적인 props 사용을 통해 내려줘야하는 props drilling 현상이 발생한다.
마치 이 영상처럼,,,,
따라서 전체적인 component에서 사용해야할 state가 있다면 별도의 global state로 관리하여 사용하도록 하는 것이 편리할 것이다.
예를 들어 로그인 처리, ui에 관한 정보 등등이 해당될 것이다.

React에서는 global state 관리는

  1. redux : 아키텍처의 구현체로 react의 global state 관리는 주로 redux를 사용
  2. context : react에서 기본으로 제공해주는 가장 간단한 방법
    redux를 쓰기 과할 때 대신 사용하기도 함

를 통해할 수 있다.

👉 context

react overview를 하는 것이기 때문에 어려운 redux 대신에 context를 이용해 global state 관리를 시도해본다.

✏️ 사용하기 전에

context는 가장 상단의 파일에서 (react.js는 App.js, Next.js는 _app.js) context component로 global state를 사용할 component들을 감싸줘야지 사용할 수 있다.
context component로 감싸진 component들은 전부 context에 접근할 수 있게되며, context component로 감싸지지 않은 component들에서는 global state를 사용할 수 없다.

_app.js 👇

import { QueryClient, QueryClientProvider } from 'react-query'
import {ReactQueryDevtools} from "react-query/devtools"
import '../styles/globals.css'
import { AuthContext } from './shared/contexts/auth';

const queryClient = new QueryClient();

function MyApp({ Component, pageProps }) {
  return (
    <작성한context.Provider
    value = {{
		// 기본값
    }}>
      <Component {...pageProps} />
  </작성한context.Provider>
  );
}

export default MyApp;

context component와 같이 component들을 감싸서 특별한 기능을 제공하는 component를 higher-order-component라고 한다.

✏️ 시작하기

React app에서 공통적으로 사용하는 값들은 공통으로 관리해주는 것이 편하기 때문에 주로 shared directory 생성 후 하위에서 관리하도록한다.
따라서 context 파일을 shared > contexts에 생성해주었다.

context는 createContext를 사용해서 생성할 수 있으며, 생성할 시에 context에서 사용할 기본값을 작성해줄 수 있다.
기본값은 작성해주지 않아도 되지만, 작성해두면 더 편하게 사용할 수 있다.

auth.js 👇

import { createContext } from "react";

export const AuthContext = createContext({
    // 기본값을 넣어둠 -> 적지 않아도 상관은 없다.
    isLoggedIn: false,
    profile: null,
    setIsLoggedIn: () => {},
    setProfile: () => {}
})

생성한 컴포넌트는 위의 "사용하기 전에"에서 설명해두었듯이 가장 상단에서 사용할 component들을 감싸주면 된다.
conext가 global로 사용할 것들의 기본값은 최상단에서 local로 관리한다.

_app.js 👇

import { useState } from 'react';
import { QueryClient, QueryClientProvider } from 'react-query'
import {ReactQueryDevtools} from "react-query/devtools"
import '../styles/globals.css'
import { AuthContext } from './shared/contexts/auth';

const queryClient = new QueryClient();

function MyApp({ Component, pageProps }) {
  // context로 사용할 것들의 기본값은 최상위에서 Local로 관리
  const [profile, setProfile] = useState(null)
  const [isLoggedIn, setIsLoggedIn] = useState(false)

  return (
    <AuthContext.Provider
    value = {{
      profile,
      isLoggedIn,
      setIsLoggedIn,
      setProfile
    }}>
    <Component {...pageProps} />
  </AuthContext.Provider>
  );
}

export default MyApp;

context는 useContext라는 hook을 통해 사용할 수 있다.

index.jsx 👇

import React, { useContext } from "react";
import { AuthContext } from "../shared/contexts/auth";

const ProfilePage = () => {
    const {profile, isLoggedIn} = useContext(AuthContext)

    return (
        <div>
            <h1>profile</h1>
            <pre>{JSON.stringify(profile, null, 2)}</pre>
        </div>
    )

}

export default ProfilePage

혹은 context를 생성할 때 useContext를 이용해서 생성하는 hook을 작성해놓고, 생성한 hook을 불러서 사용할 수도 있다.

auth.jsx 👇

import { createContext, useContext } from "react";

export const AuthContext = createContext({
    // 기본값을 넣어둠 -> 적지 않아도 상관은 없다.
    isLoggedIn: false,
    profile: null,
    setIsLoggedIn: () => {},
    setProfile: () => {}
})

export const useAuth = () => useContext(AuthContext)

index.jsx 👇

import React from "react";
import { useAuth } from "../shared/contexts/auth";

const ProfilePage = () => {
    const {profile, isLoggedIn} = useAuth()

    return (
        <div>
            <h1>profile</h1>
            <pre>{JSON.stringify(profile, null, 2)}</pre>
        </div>
    )

}

export default ProfilePage

생성한 context를 사용하면 props를 통해 값을 전달하지 않아도 동일한 값을 서로 다른 component에서 사용할 수 있게 된다.

index.jsx 👇

import React from "react";
import { useAuth } from "../shared/contexts/auth";

const ProfilePage = () => {
    const {profile, isLoggedIn} = useAuth()

    return (
        <div>
            <h1>profile</h1>
            <pre>{JSON.stringify(profile, null, 2)}</pre>
        </div>
    )

}

export default ProfilePage

update.jsx 👇

import Link from "next/link"
import { useAuth } from "../shared/contexts/auth"

const UpdateProfilePage = () => {
    const {profile, setProfile, setIsLoggedIn} = useAuth()

    const toggleProfile = () => {
        if (profile) {
            setProfile(null)
            setIsLoggedIn(false)
        } else {
            setProfile("Jhon")
            setIsLoggedIn(true)
        }
    }

    return (
        <div>
            <Link href={"/profile"}>
                <button>go to profile page</button>
            </Link>
            <pre>{JSON.stringify(profile, null, 2)}</pre>
            <button onClick={toggleProfile}>click to toggle</button>
        </div>
    )
}

export default UpdateProfilePage


실행 결과에서 확인할 수 있듯이, profile과 isLoggedIn의 값을 props로 전달하지 않았지만 context를 사용하는 다른 페이지에서 일어난 변경 사항이 페이지가 전환되어도 유지되는 것을 확인할 수 있다.

좋은 웹페이지 즐겨찾기