전역적으로 사용되는 데이터를 하위 컴포넌트로 일일히 전달하지 않고 여러 컴포넌트에서 공유할 수 있도록 합니다.
Context를 사용하면 컴포넌트를 재사용하기 어렵기 때문에 필요한 곳에 사용해야합니다.
또한 state가 여러 번 변경되는 경우 context가 최적화 되지 않을 수도 있습니다.
단순히 Prop drilling을 피하기 위한 목적으로 사용하는 것은 추천하지 않습니다.
React Context는 전역 데이터를 담고 있는 하나의 저장 공간입니다.
createContext는 컨텍스트에 디폴트로 저장할 값을 받습니다.
// store/MyContext.js
import { createContext } from 'react';
// createContext
export const MyContext = React.createContext(null);
컴포넌트를 Provider로 감싸주면, 감싸진 컴포넌트의 하위에 있는 모든 컴포넌트들은 React Context에 저장되어 있는 전역 데이터에 접근할 수 있습니다.
value를 사용하여 컨텍스트의 초기 상태나 값을 설정할 수 있습니다.
// App.js
import { useState } from 'react';
import { MyContext } from './store/MyContext';
import MyComponent from './components/MyComponent';
function App() {
const [state, setState] = useState(false)
return (
// Provider
<MyContext.Provider value={{state, setState}}>
<MyComponent />
</MyContext.Provider>
)
}
컴포넌트 최상단에 있는 useContext()의 반환값에서 Context에 저장되어 있는 전역 데이터에 접근할 수 있습니다.
useContext()는 createContext의 반환값을 받습니다.
// MyComponent.js
import React, { useContext } from 'react';
import { MyContext } from './store/MyContext';
const MyComponent = () => {
// useContext
const data = useContext(MyContext)
console.log(data)
// {state: false, setState: f()}
return (
<>
<h1>Hello World</h1>
<>
)
}
createContext() 기본값에는 IDE 자동 완성을 위해서 사용할 속성을 모두 넣어주는 것이 좋습니다.
Provider를 로그인 state를 관리하는 wrapper 컴포넌트로 사용하면 전체 코드를 간결하게 사용할 수 있습니다.
// store/auth-context.js
import React from "react";
const AuthContext = React.createContext({
// createContext에 사용할 속성을 모두 추가
isLoggedIn: false,
onLogout: () => {},
onLogin: (email, password) => {},
});
// 전체 인증 state를 관리
export const AuthContextProvider = (props) => {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const logoutHandler = () => {
setIsLoggedIn(false);
};
const loginHandler = () => {
setIsLoggedIn(true);
};
return (
<AuthContext.Provider
value={{
isLoggedIn: isLoggedIn,
onLogout: logoutHandler,
onLogin: loginHandler,
}}
>
{props.children}
</AuthContext.Provider>
);
};
export default AuthContext;
index.js에 Provider 컴포넌트를 wrapper로 사용하여 모든 컴포넌트에서 로그인 state를 사용할 수 있도록 합니다.
// index.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import { AuthContextProvider } from "./store/auth-context";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<AuthContextProvider>
<App />
</AuthContextProvider>
);
isLoggedIn, onLogout 그리고 onLogin이 필요한 컴포넌트에 useContext로 불러와 사용합니다.
// App.js
import React, { useContext } from "react";
import Login from "./components/Login/Login";
import Home from "./components/Home/Home";
import MainHeader from "./components/MainHeader/MainHeader";
import AuthContext from "./store/auth-context";
function App() {
const cntx = useContext(AuthContext);
return (
<React.Fragment>
<MainHeader />
<main>
{!cntx.isLoggedIn && <Login />}
{cntx.isLoggedIn && <Home />}
</main>
</React.Fragment>
);
}
export default App;
재사용이 필요한 Presentational 컴포넌트 같은 경우는 context보다 props를 사용하는 것이 좋습니다.
예를 들어 Button 컴포넌트를 만들어 재사용중이라면 Button 컴포넌트는 쓰임에 따라 다른 기능을 수행하게 됩니다.
물론 Button 컴포넌트 내부에 useContext()를 사용할 수 있지만 하지만 버튼이 클릭될 때마다 useContext를 사용하는 한가지 기능 외에 다른 일은 할 수 없게 됩니다.
import React from 'react';
import classes from './Button.module.css';
const Button = (props) => {
return (
<button
type={props.type || 'button'}
className={`${classes.button} ${props.className}`}
// props 대신 context를 사용하여 로그아웃 버튼으로 사용 할 수 있지만,
// 그렇게 사용하면 다른 용도의 버튼으로 쓸 수 없음
onClick={props.onClick}
disabled={props.disabled}
>
{props.children}
</button>
);
};
export default Button;