상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달하거나 받으면서 항상 props chain을 이용했었다.
이때 유쾌하지 못한 부분이 발생하는데 여러 하위 컴포넌트 중 낮은 레벨 수준의 컴포넌트로 데이터를 전달할 때 해당 state를 직접 사용하지 않는 컴포넌트들도 props를 통해서 state를 전달하는 로직을 수행해야하는 점이다.
이번엔 간단한 로그인 프로그램을 이용하여 하위 컴포넌트 중 해당 데이터를 사용하는 컴포넌트에서만 데이터에 접근할 수 있도록 Context를 이용해 보겠다.
먼저 src 디렉토리에 store라는 디렉토리를 만들자
여기에 Context를 만들어 저장할 것이다.
// AuthContext.js
import React from "react";
// context 기본 객체 생성
const AuthContext = React.createContext({
isLoggedIn: false,
onLogout: () => {},
});
export default AuthContext;
여기서 해당 코드는 컴포넌트는 아닌 객체이다.
하지만 나중에 컴포넌트를 포함 할 것이다.
정의한 Context를 사용하려면 이를 리스닝할 요소들을 JSX로 감싸주어야 한다.
const [isLoggedIn, setIsLoggedIn] = useState(false);
const logoutHandler = () => {
localStorage.removeItem("isLoggedIn");
setIsLoggedIn(false);
};
<...>
<AuthContext.Provider
value={{
isLoggedIn: isLoggedIn,
onLogout: logoutHandler,
}}
>
<MainHeader />
<main>
{!isLoggedIn && <Login onLogin={loginHandler} />}
{isLoggedIn && <Home onLogout={logoutHandler} />}
</main>
</AuthContext.Provider>
위에서 언급했듯이 AuthContext는 컴포넌트가 아니다.
하지만 Context의 속성인 Provider를 이용해서 컴포넌트로 사용할 수 있다.
Provider는 말 그대로 공급자로 감싸진 모든 컴포넌트에서 해당 context를 이용할 수 있게 해준다.
이후 value prop를 이용하여 이전에 정의해둔 Context 속 state값을 실제 state 값으로 초기화하여 사용한다.
공급했다면 useContext 훅을 이용하여 사용해보자.
import React, { useContext } from "react";
import classes from "./Navigation.module.css";
import AuthContext from "../../store/auth-context";
const Navigation = () => {
const ctx = useContext(AuthContext);
return (
<nav className={classes.nav}>
<ul>
{ctx.isLoggedIn && (
<li>
<a href="/">Users</a>
</li>
)}
{ctx.isLoggedIn && (
<li>
<a href="/">Admin</a>
</li>
)}
{ctx.isLoggedIn && (
<li>
<button onClick={ctx.onLogout}>Logout</button>
</li>
)}
</ul>
</nav>
);
};
이렇게 수행하면 Context를 이용해서 원하는 state값을 원하는 하위 컴포넌트에서 prop chain을 이용하지 않고 직접 접근할 수 있다.
Context는 매우 유용해 보이지만 많은 컴포넌트들을 거쳐 prop을 전달해야하는 경우 외에 무분별한 사용은 오히려 독이 될 수 있다.
prop은 컴포넌트를 구성하고 그것들을 재사용할 수 있도록 하는 매커니즘이기 때문이다.