Context API는 React 컴포넌트 트리 전체에서 데이터를 공유할 수 있는 방법을 제공합니다.
이를 통해 중첩된 구조에서 데이터를 전달하는 데 있어서 불필요한 props drilling을 방지할 수 있습니다.
props drilling은 React 컴포넌트 구조에서 하위 컴포넌트로 데이터를 전달하기 위해 상위 컴포넌트를 거치는 것을 의미합니다.
이는 컴포넌트 구조가 깊어지면서 코드가 복잡해지고 유지보수가 어려워지는 문제를 유발합니다. Context API는 이러한 문제점을 해결하기 위한 방법 중 하나입니다.
props drilling 상태 내리꽂기 -> 코드 가독성 떨어뜨리고 유지보수 어렵게 함
=> 대신 Context API (React 기본 제공), Redux 라는 상태 관리 API 씀
Context를 사용하기 위해서는 createContext() 함수를 사용하여 Context 객체를 생성해야 합니다.
이후 Context.Provider 컴포넌트를 이용해 하위 컴포넌트에게 데이터를 전달할 수 있습니다.
Provider는 value 라는 속성을 통해 데이터를 전달합니다.
Context 객체 안에 Provider, Consumer 객체 이용해서 적절히 사용하면 됨
Context API는 Redux와 같은 상태 관리 라이브러리를 대체할 수는 없지만, 단순한 상태 관리에는 편리하게 사용할 수 있습니다.
규모가 큰 프로젝트는 Context API로는 한계가 있음 Redux 사용해야함
src/contextAPI/css/style.css
/* context api를 이용한 하위 모든 컴포넌트에게 라이트 테마, 다크 테마 적용시키기 */
/* 라이트 테마에 대한 스타일 정의 */
.light-theme {
background-color: #fff;
color: #000;
padding: 20px;
text-align: center;
}
/* 다크 테마에 대한 스타일 정의 */
.dart-theme {
background-color: #000;
color: #fff;
padding: 20px;
text-align: center;
}
src/contextAPI/ThemeContext.js
// ThemeContext.js
// 내가 만들고자 하는 Context를 정의할 파일
import { createContext, useState } from "react";
// Context API는 React에서 컴포넌트 트리 전체에 걸쳐 전역적인 데이터를 공유하고 전달하기 위한 방법
// -> 깊이 중첩된 컴포넌트들 간에도 데이터를 간편하게 전달할 수 있다.
// -> props drilling(상태 내리꽂기)을 피하기 위해 사용
// 사용방법
// createContext() 함수를 사용하여 Context 객체를 생성할 수 있음
// -> Provider와 Consumer를 포함
// 1. Provider : 컨텍스트를 사용하는 컴포넌트들에게 값을 제공하는 역할.
// 값을 전달할 (자식)컴포넌트들을 감싸고, 'value'라는 prop을 통해서 전달할 값을 지정
// 2. Consumer : 컨텍스트의 값을 사용하는 컴포넌트.
// 최근에는 React 에서는 useContext Hook 사용하여 컨텍스트 값을 가져와 사용함
// => Consumer를 부르지 않아도 컨덱스트 값을 부를 수 있는 Hook
// ThemeContext라는 이름의 새 Context 생성
const ThemeContext = createContext(); // 자동 완성 import
// Context 객체를 이용할 컴포넌트 생성
// ThemeProvider 컴포넌트 생성
const ThemeProvider = ({children}) => {
// App.js 에서 불러다가 쓸 거임 (최상위 컴포넌트로)
// 전역적인 상태를 줄거임
// 테마에 관한 상태 정의, 초기값은 light
const [theme, setTheme] = useState('light');
// 버튼 클릭 시 다크 모드 (다른 파일에 만들 거)
// 테마를 토글 시켜줄 함수를 정의해줄 거
const toggleTheme = () => {
// 함수가 실행될 때마다 setTheme 상태 변경 시켜줄 거
setTheme(theme == 'light' ? 'dark' : 'light');
}
// 화면에 렌더링 시켜줄 무언가
// ThemeContext.Provider를 통해서 '상태 theme' 과 'toggleTheme 함수'를 제공
return(
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{/* ThemeContext가 제공하는 provider를 통해서 만들어준 상태 theme, toggleTheme 함수를 전달해줄 거 */}
{/* 이 안에 들어오는 모든 컴포넌트들에게 */}
{/* value 라는 prop 에 theme, toggleTheme 두개를 전달해줘야함 */}
{children}
{/* ThemeProvider 를 App.js 에서 불러다 쓸건데 ThemeProvider 태그에 불러다 쓸 거
태그 안에 불린 애들이 children 안에 다 들어올 거 */}
{/* children 안에 다 들어와서 theme, toggleTheme 다 쓸 수 있게 */}
</ThemeContext.Provider>
);
}
// 두 개 내보낼 거임
export {ThemeContext, ThemeProvider};
contextAPI/ThemeToggle.js
// ThemeToggle.js
import { useContext, useState } from "react";
import { ThemeContext } from "./ThemeContext";
// 테마를 다크모드 라이트모드 토글하는 버튼 컴포넌트를 만들어서 내보낼거임
const ThemeToggle = () => {
// App.js ThemeProvider 태그 안에 들어갈 첫번째 자식
// theme, toggleTheme 이용할 수 있음
// => 버튼 누르면 toggle 되게 해줄 거임
const {toggleTheme} = useContext(ThemeContext);
// useContext Hook을 사용하여 ThemeContext의 값 중 toggleTheme 이라는 함수를 가져온 거
// 버튼 클릭 시 toggleTheme 함수 호출
return <button onClick={toggleTheme}>테마 토글</button>
}
export default ThemeToggle;
ThemeComponent.js
// ThemeComponent.js
import { useContext } from "react";
import { ThemeContext } from "./ThemeContext";
// CSS 파일 임포트 해야함
import './css/style.css';
import ThemeChildComponent from "./ThemeChildComponent";
// 현재 테마 상태에 따라서 스타일을 적용하고 보여줄 컴포넌트
const ThemeComponent = () => {
// App.js ThemeProvider 두번째 자식
// theme 상태나 toggleTheme 함수 사용할 수 있음
// useContext 함수 사용해서 상태에 따라 클래스 다르게 줄거임
const {theme} = useContext(ThemeContext);
// 둘 다 쓰고싶으면 const {theme,toggleTheme} = useContext(ThemeContext); 이렇게 작성하면 됨
// useContext Hook을 사용하여 ThemeContext의 값 중 theme 상태값 가져오기
// 현재 테마에 따라 클래스 이름 설정
const themeStyle = theme == 'light' ? 'light-theme' : 'dart-theme';
return (
<>
<div className={themeStyle}>
{/* css 불러줄거임 light-theme 이거나 dart-theme */}
현재 {theme} 테마 적용 중 입니다.
{/* 처음 값 light */}
</div>
{/* 새로운 자식 컴포넌트 만들어주기 3레벨 */}
<ThemeChildComponent />
{/* propsdrilling 이 없어도 잘 적용됨 */}
</>
);
}
export default ThemeComponent;
ThemeChildComponent.js
// ThemeChildComponent.js
import { useContext } from "react";
import { ThemeContext } from "./ThemeContext";
// 3레벨 자식 컴포넌트 만들기
// 1레벨이 가진 상태 값이나 함수를 사용할 수 있는지
const ThemeChildComponent = () => {
const {theme} = useContext(ThemeContext);
const modeName = theme == 'light' ? '라이트' : '다크';
return <h1>{modeName} 테마 적용중이에요~</h1>
}
export default ThemeChildComponent;
App.js
import logo from './logo.svg';
import './App.css';
import { ThemeProvider } from './contextAPI/ThemeContext';
import ThemeToggle from './contextAPI/ThemeToggle';
import ThemeComponent from './contextAPI/ThemeComponent';
// 터미널 ctrl + shift + \` (백틱)으로 열고
// 터미널에서 yarn start 서버 켜기
// 터미널에서 ctrl + c 서버 끄기
function App() {
return (
<div className='App'>
<ThemeProvider>
{/* ThemeContext는 다른 곳에서 쓸 거임 useContext 이용해서 이 값을 사용하는 주체가 되는 자식 컴포넌트들이 ThemeContext 이용할거임 */}
{/* 자식 컴포넌트 여러 개 나열할 수 있음 그럼 ThemeProvider 함수의 children 으로 들어가고 */}
{/* 그 children 들이 return 값 안에 children 에 들어감 */}
{/* 자식 컴포넌트들이 theme, toggleTheme 이용할 수 있음 */}
<ThemeToggle />
<ThemeComponent />
</ThemeProvider>
</div>
);
}
export default App;
css import 중요함, 모든 import 들이 빠지면 안됨
ThemeProvider 안에 자식 컴포넌트들 부르면 children 안에 들어가서 상태, 함수 다 사용할 수 있게 해줌
상태 내리꽂기를 안해도 3레벨에 있는 자식이 ThemeComponent 가 받은 상태, 함수를 사용할 수 있음