useContext 를 사용하여 drilling 이 없도록.
Children 컴포넌트에서 제약사항 없이 부모가 내려준 props 를 사용할 수 있도록 하겠습니다.
1. useContext
useContext 는 store 와 기능이 매우 흡사함.
다만, 작은 규모의 프로젝트 혹은 개인프로젝트에서만 사용하는것을 권장한다.
그 외에는 store 를 사용하자 (redux, zustand ... etc)
그 이유는 아래 내용을 보면 알 수 있다. 가봅시다.
2. createContext & Provider
// 1. 테마 컨텍스트 생성
const ThemeContext = createContext();
//TypeScript
const ThemeContext = React.createContext<ThemeContextType | undefined>(
undefined
);
const UseContext = () => {
const [theme, setTheme] = useState("light");
const toggleTheme = () => {
setTheme(theme === "light" ? "dark" : "light");
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
<ThemeComponent_1 />
</ThemeContext.Provider>
);
};
처음에 컴포넌트 바깥에서 Context를 생성 (createContext) 해주었다.
그 다음에는
<ThemeContext.Provider value={{state, action}}>
<ThemeComponent_1 />
</ThemeContext.Provider>
이렇게 Provider로 감싸서 props로 내려주었다. (여기까지는 일반 Props 와 뭐가다른데? 할 수 있다.)
차이점은 2번째 Drilling 에서부터 나타난다.
3. Childrens.
ThemeComponent_1 컴포넌트를 아래와 같이 선언하고 ThemeComponent_2에서 context로 내려준 props 들을 사용할 수 있다.
UseContext > ThemeComponent_1 > ThemeComponent_2 가 되는 꼴이다
//drilling 용 컴포넌트
const ThemeComponent_1 = () => {
return <ThemeComponent_2 />;
};
//실제 context를 사용하는 컴포넌트
const ThemeComponent_2 = () => {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<div
className={`w-full mx-auto px-4 py-8 min-h-screen transition-all duration-300 ${
theme === "light" ? "bg-gray-50" : "bg-gray-900 text-white"
}`}
>
<h1>현재 테마: {theme}</h1>
<button onClick={toggleTheme}>테마 변경</button>
</div>
);
};
[테마 변경] 버튼을 클릭 하면 light <> dark 로 전환이 되면서 배경색이 변경되도록 수정하였다.
전역적으로 사용하려면 App.jsx 혹은 Index.jsx 에 선언해놓고 Header.jsx 에서 사용하면 될 것이다.
Epiloge
확실히 useContext 를 통하여 사용할 수 있는 부분이 눈에 보인다. (가령 테마라던가...테마라던가...테마라던가.)
하지만 규모가 조금이라도 커지면 useContext 를 사용할 수 없을 것 같다.
유지보수 측면에서 너무 큰 비용이 들어갈 것으로 보임.
그래서 결국에는 store (Redux, Zustand) 를 사용하게 될 것 같다.
전체 코드
TypeScript 로 작성해서 간단하게 type 을 선언하여 사용하였다.
import React, { useContext, useState } from "react";
import { FaSun, FaMoon } from "react-icons/fa";
interface ThemeContextType {
theme: string;
toggleTheme: () => void;
}
// 1. 테마 컨텍스트 생성
const ThemeContext = React.createContext<ThemeContextType | undefined>(
undefined
);
const UseContext = () => {
const [theme, setTheme] = useState("light");
const toggleTheme = () => {
setTheme(theme === "light" ? "dark" : "light");
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
<ThemeComponent_1 />
</ThemeContext.Provider>
);
};
//drilling 용 컴포넌트
const ThemeComponent_1 = () => {
return <ThemeComponent_2 />;
};
//실제 context를 사용하는 컴포넌트
const ThemeComponent_2 = () => {
const context = useContext(ThemeContext);
if (!context)
throw new Error("ThemeContext must be used within ThemeProvider");
const { theme, toggleTheme } = context;
return (
<div
className={`w-full mx-auto px-4 py-8 min-h-screen transition-all duration-300 ${
theme === "light" ? "bg-gray-50" : "bg-gray-900 text-white"
}`}
>
<button
onClick={toggleTheme}
className={`p-2 rounded-full ${
theme === "light"
? "bg-gray-200 hover:bg-gray-300"
: "bg-gray-700 hover:bg-gray-600"
} transition-colors duration-200`}
aria-label="테마 변경"
>
{theme === "light" ? (
<FaMoon className="w-5 h-5 text-gray-700" />
) : (
<FaSun className="w-5 h-5 text-yellow-300" />
)}
</button>
</div>
);
};
export default UseContext;
'FrontEnd > React.js' 카테고리의 다른 글
[React / scss] import 오류 해결하기 > use 쓰기 (0) | 2025.03.17 |
---|---|
[React-Query] 리액트 쿼리 사용해보기 - 캐싱을 통한 최적화 (0) | 2025.03.12 |
[React.js] useTransition & useDeferredValue 함께 사용하자 (0) | 2025.03.10 |
[React] Streaming 처리하기 3 - abort 기능 추가 (0) | 2025.03.06 |
[React.js] Stream 데이터 fetch로 처리하기 - 2 (0) | 2025.02.28 |