Context API는 리액트 프로젝트에서 전역적으로 사용할 데이터가 있을 때 유용한 기능이다. 이를테면 사용자 로그인 정보, 어플리케이션 환경 설정, 테마 등이 있다.
Context API를 이용한 전역 상태 관리 흐름 이해하기
리액트 어플리케이션에서 여기저기에 필요한 데이터는 보통 최상위 컴포넌트인 App의 state에 넣어서 관리한다. 그리고 최상위 컴포넌트에서 해당 데이터를 사용하는 컴포넌트 까지 props형태로 전달해주는데 그 경로가 복잡해지는 경우 관리하기가 매우 복잡하다. 이런 경우 Context API를 사용하면 Context를 만들어 단 한 번에 원하는 값을 받아와서 사용이 가능하다.
Context API 사용법 익히기
src 디렉터리에 contexts 디렉터리를 만든 뒤 그 안에 color.js라는 파일을 만든다. 다음 파일을 생성한다.
contexts/color.js
import {createContext} from 'react';
const ColorContext = createContext({color: 'black'});
export default ColorContext;
새 Context를 만들 때는 createContext 함수를 사용한다. 파라미터에는 해당 Context의 기본 상태를 지정한다.
Consumer 사용하기
이번에는 ColorBox라는 컴포넌트를 만들어서 ColorContext 안에 들어 있는 색상을 보여 주겠다. 이때 색상을 props로 받아 오는 것이 아니라 ColorContext 안에 들어 있는 Consumer라는 컴포넌트를 통해 색상을 조회하겠다.
ColorBox.js
import React from 'react';
import ColorContext from '../contexts/color';
const ColorBox = () => {
return(
<ColorContext.Consumer>
{value => (
<div
style={{
width: '64px',
height: '64px',
background: value.color
}}
/>
)}
</ColorContext.Consumer>
)
}
export default ColorBox;
Consumer 사이에 중괄호를 열어서 그 안에 함수를 넣어 주었다. 이러한 패턴을 Function as a child, 혹은 Render Props라고 한다. 컴포넌트의 children이 있어야 할 자리에 일반 JSX 혹은 문자열이 아닌 함수를 전달하는 것이다.
Render Props
import React from 'react';
const RenderPropsSample = ({children}) => {
return <div>결과: {children(5)}</div>
};
export default RenderPropsSample;
위와 같은 컴포넌트가 있다면 추후 사용할 때 다음과 같이 사용할 수 있다.
<RenderPropsSample> {value => 2*value} </RenderPropsSample>
Provider
provider를 사용하면 Context의 value를 변경할 수 있다. App 컴포넌트를 다음과 같이 수정해보자.
import React from 'react';
import ColorBox from './components/ColorBox';
import ColorContext from './contexts/color';
function App() {
return (
<ColorContext.Provider value={{color: 'red'}}>
<div className="App">
<ColorBox/>
</div>
</ColorContext.Provider>
);
}
export default App;
기존에 createContext 함수를 사용할 때는 파라미터로 Context의 기본 값을 넣어주었다. 이 기본값은 Provider를 사용하지 않았을 때만 사용된다. 만약 Provider는 사용했지만 value를 명시하지 않았다면, 이 기본값을 사용하지 않았기 때문에 오류가 발생한다.
동적 Context 사용하기
지금까지 배운 내용으로는 고정적인 값만 사용할 수 있다. 이번에는 Context의 값을 업데이트해야 하는 경우 어떻게 해야 하는지 알아보겠다.
context 파일 수정하기
Context의 value에는 상태 값 뿐만 아니라 함수를 전달할 수도 있다.
contexts/color.js
import React, {createContext, useState} from 'react';
const ColorContext = createContext({
state: {color: 'black', subcolor:'red'},
action: {
setColor: () => {},
setSubColor: () => {}
}
});
const ColorProvider = ({children}) => {
const [color, setColor] = useState('black');
const [subcolor, setSubcolor] = useState('red');
const value= {
state: {color, subcolor},
actions: {setColor, setSubcolor}
};
return (
<ColorContext.Provider value={value}>{children}</ColorContext.Provider>
);
};
// const colorConsumer = ColorContext.Consumer와 같음
const {Consumer:ColorConsumer} = ColorContext;
export {ColorProvider, ColorConsumer};
export default ColorContext;
위 파일에서 ColorProvider라는 컴포넌트를 새로 작성해 주었다. 그리고 그 컴포넌트에서는 ColorContext.Provider를 렌더링하고 있다. 이 Provider의 value에는 상태는 state로, 업데이트 함수는 actions로 묶어서 전달하고 있다.
createContext의 기본값은 실제 Provider의 value에 넣는 객체의 형태와 일치시켜 주는 것이 좋다. 그렇게 하면 Context코드를 볼 때 내부 값이 어떻게 구성되어 있는지 파악하기도 쉽고, 실수로 Provider를 사용하지 않았을 때 리액트 어플리케이션에서 에러가 발생하지 않는다.
색상 선택 컴포넌트 만들기
이번에는 context의 action에 넣어 준 함수를 호출하는 컴포넌트를 만들어 보겠다.
import React from 'react';
import {ColorConsumer} from '../contexts/color';
const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];
const SelectColors = () => {
return(
<div>
<h2>색상을 선택하세요.</h2>
<ColorConsumer>
{({actions}) => (
<div style={{display: 'flex'}}>
{colors.map(color => (
<div
key={color}
style={{
background: color,
width: '24px',
height: '24px',
cursor: 'pointer'
}}
onClick={()=>actions.setColor(color)}
onContextmenu={e=>{
e.preventDefault();
actions.setSubcolor(color);
}}
/>
))}
</div>
)}
</ColorConsumer>
<hr/>
</div>
);
};
export default SelectColors;
마우스 오른쪽 버튼 클릭 이벤트는 onContextMenu를 사용하면 된다. 오른쪽 클릭 시 원래 브라우저 메뉴가 나타나지만, 여기서 e.preventDefault()를 호출하면 메뉴가 뜨지 않는다.
Consumer 대신 Hook 또는 static contextType 사용하기
이번에는 Context에 있는 값을 사용할 때 Consumer 대신 다른 방식을 사용하여 값을 받아 오는 방법을 알아보겠다.
useContext Hook 사용하기
리액트에 내장되어 있는 Hooks 중에서 useContext라는 Hook을 사용하면, 함수형 컴포넌트에서 Context를 아주 편하게 사용할수 있다. ColorBox 컴포넌트 코드를 다음과 같이 수정하자.
import React,{useContext} from 'react';
import ColorContext from '../contexts/color';
const ColorBox = () => {
const {state} = useContext(ColorContext);
return(
<>
<div
style={{
width: '64px',
height: '64px',
background: state.color
}}
/>
<div
style={{
width: '32px',
height: '32px',
background: state.subcolor
}}
/>
</>
);
};
export default ColorBox;
만약 children에 함수를 전달하는 Render Props 패턴이 불편하다면, useContext Hook을 사용하여 훨씬 편하게 Context값을 조회할 수 있다. 다만 Hook은 함수형 컴포넌트에서만 사용할 수 있다는 점에 주의하자.
'Frontend > React' 카테고리의 다른 글
2021-06-22 :: React로 스탑워치 만들기 (0) | 2021.06.22 |
---|---|
2021-05-21 :: useEffect (0) | 2021.05.25 |
2021-05-11 :: 컴포넌트 성능 최적화2 (0) | 2021.05.11 |
2021-05-07 :: 컴포넌트 성능 최적화1 (0) | 2021.05.11 |
2021-05-06 :: Todo Project1 (0) | 2021.05.06 |