Context API란 전역 상태 관리를 하기 위해서 사용합니다. 먼저 기존 리액트에서 Context API와 Redux와 같은 상태 관리 라이브러리가 없을 때 어떻게 최상위 컴포넌트와 최하단 컴포넌트가 데이터를 주고 받는지 생각해볼 필요가 있습니다. 먼저 Props Drilling에 대한 개념을 살펴봐야 합니다.
Props Drilling이란 props를 오로지 하위 컴포넌트에 전달하는 용도로만 쓰는 상황을 의미합니다. React Component 트리의 한 부분에서 다른 부분으로 데이터를 전달하는 과정에서 자주 발생합니다.
다음과 같은 상황에서 예시를 들겠습니다.
1. G 컴포넌트는 전역 상태를 업데이트 시킨다.
2. F 컴포넌트와 J 컴포넌트는 업데이트 된 상태를 렌더링시킨다.
App이 지닌 value 값이 F, J 컴포넌트에 전달하려면 여러 컴포넌트를 거쳐야 합니다.
또한 G 컴포넌트에 상태 업데이트 함수를 전달하는 경우 또한 마찬가지입니다.
[F] App => A => B => F
[J] App => H => J
[G] App => A => B => E => G
위와 같은 방식으로 props를 전달하면 불필요한 과정이 너무 많습니다. 또한 이러한 방식은 유지 보수성이 매우 낮을 수 밖에 없습니다.
이러한 Props Rendering 문제를 어떻게 해결해야 할까요? 답은 전역적으로 상태 관리를 하면 된다 입니다.
다음과 같이 Context API를 사용하면 됩니다.
Context API를 사용할 때는 세 가지를 기억하면 됩니다.
1. 새로운 context를 createContext 함수로 만들어주고 파라미터에는 해당 context의 default state를 지정해줍니다.
2. 사용하고자 하는 Component에서 Consumer 사이에 Render Props 패턴을 사용합니다.
3. Provider로 Context의 value값을 변경합니다. 이때 Provider는 Consumer를 감싼 형태여야만 합니다.
주의) createContext에서 파라미터로 default state를 지정해주었는데, 이 값은 Provider를 사용하지 않았을 때만 사용됩니다!
Context의 value에는 상태 값 뿐만 아니라 함수를 전달해줄 수도 있는데요. 앞선 예시에서 G 컴포넌트에 상태 업데이트 함수를 전달할 때처럼 함수를 전달할 수 있습니다.
함수를 전달할 때는 다음과 같은 방식으로 전달합니다.
Provider의 value에 상태는 state로, 업데이트 함수는 actions로 묶어서 전달합니다.
createContext의 기본값 또한 Provider의 value에 넣는 객체의 형태와 일치시켜 줍니다.
<contexts/theme.js>
import {createContext, useState} from 'react';
const ThemeContext = createContext({
state { color: 'black', bgColor: 'white' },
actions: {
setColor: () => {},
setBgColor: () => {},
}
})
const ThemeProvider = ({children}) => {
const [color, setColor] = useState('black');
const [bgColor, setBgColor] = useState('white');
const value = {
state : {color, bgColor},
actions: {setColor, setBgColor}
};
return (
<ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
);
}
const {Consumer: ThemeConsumer} = ThemeContext;
export {ThemeProvider, ThemeConsumber, ThemeConsumer}
export default ThemeContext;
리액트에 내장되어 있는 Hooks 중 useContext라는 Hook을 사용한다면 Consumer를 사용할 필요 없이, 필요로 하는 데이터를 사용할 수 있습니다.
const { state } = useContext(#만든 Context)
useContext 방식은 함수를 전달하는 Render Props 패턴이 불편할 때 사용하면 좋습니다.
하지만 오직 함수형 컴포넌트에서만 사용이 가능합니다.