React에서의 local state는 props와 state로 나뉜다.
props는 상속받은 상태에 대한 관리이고, state는 component가 생성한 상태에 대한 관리이다.
✏️ component 상태 관리
component에서 다른 component로 state를 전달해주기 위해서는 반복적인 props 사용을 통해 내려줘야하는 props drilling 현상이 발생한다.
마치 이 영상처럼,,,,
따라서 전체적인 component에서 사용해야할 state가 있다면 별도의 global state로 관리하여 사용하도록 하는 것이 편리할 것이다.
예를 들어 로그인 처리, ui에 관한 정보 등등이 해당될 것이다.
React에서는 global state 관리는
를 통해할 수 있다.
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를 사용하는 다른 페이지에서 일어난 변경 사항이 페이지가 전환되어도 유지되는 것을 확인할 수 있다.