context API는 리액트 프로젝트에서 전역적으로 사용할 데이터가 있을 때 유용한 기능이다.
기존에는 최상위 컴포넌트에서 여러 컴포넌트를 거쳐 props 로 원하는 상태와 함수를 전달 했지만, Context API를 사용하면 Context 를 만들어 단 한번에 원하는 값을 받아 와서 사용할 수 있다.
리액트 프로젝트 새로 생성
$ yarn create react-app context-tutorial
새로운 Context생성
<contexts/color.js>
import { createContext } from 'react';
const ColorContext = createContext({color: 'black'});
export default ColorContext;
새 Context를 만들 때는 createContext 함수를 사용한다. 파라미터에는 해당 Context의 기본 상태를 지정한다.
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;
색상을 props 로 받아 오는 것이 아니아 ColorContext 안에 들어 있는 consumer라는 컴포넌트를 통해 색사을 조회
-> Consumer 사이에 중괄호를 열어서 그 안에 함수를 넣어준다. 이러한 패턴을 Function as a child, 혹은 Render Props라고 한다. 컴포넌트의 children이 있어야할 자리에 일반 JSX혹은 문자열이 아닌 함수를 전달하는것
<App.js>
import React from 'react';
import ColorBox from './components/ColorBox';
import ColorContext from './contexts/color';
const App = () => {
return (
<ColorContext.Provider value= {{ color : 'red'}}>
<div>
<ColorBox />
</div>
</ColorContext.Provider>
);
};
export default App;
기존에 createContext 함수를 사용할 때는 파라미터로 Context 의 기본값을 넣어 주었다.
이 기본값은 Provider를 사용하지 않을 때만 사용된다. 만약 Providr 는 사용했는데 value를 명시하지 않았다면, 이 기본값을 사용하지 않기 때문에 오류가 발생한다.
Provider 를 사용할 때는 value 값을 명시해 주어야 제대로 작동한다는 것을 명심하자
context의 value 에는 무조건 상태 값만 있어야 하는 것은 아니다. 함수를 전달해 줄 수도 있다.
<contexts/color.js>
import { createContext, useState } from 'react';
const ColorContext = createContext({
state: { color: 'black', subcolor: 'red' },
actions: {
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;
// ColorProvider와 ColorCunsumer 내보내기
export { ColorProvider, ColorConsumer };
export default ColorContext;
ColorProvider 라는 컴포넌트를 새로 작성해 주었다. 그리고 그 컴포넌트에서 ColorContext.Provider를 렌더링 하고 있다.
이 Provider의 value 에는 상태는 state로, 업데이트 함수는 actions 로 묶어서 전달하고 있다.
<components/ColorBox.js>
import React from 'react';
import {ColorConsumer} from '../contexts/color';
const ColorBox = () => {
return (
<ColorConsumer>
{value => (
<>
<div
style={{
width: '64px',
height: '64px',
background: value.state.color
}}
/>
<div
style={{
width: '32px',
height: '32px',
background: value.state.subcolor
}}
/>
</>
)}
</ColorConsumer>
);
};
export default ColorBox;
App.js 에 렌더링 해준다
-> 마우스 좌클릭클릭은 큰 정사각형의 색상을 변경하고, 마우스 우클릭은 작은 정사각형의 색상을 변경하도록 구현.
<components/SelectColors.js>
import React from 'react';
import {ColorConsumer} from '../contexts/color';
const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'imdigo', '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()를 호출하면 메뉴가 뜨지 않는다.
기존에는 컴포넌트 간에 상태를 교류해야 할때 무조건 부모 -> 자식 흐름으로 props 를 통해 전달 해주었다. 이제는 Context API 를 통해 더욱 쉽게 상태를 교류할 수 있게 되었다.
전역적으로 여기저기서 사용되는 상태가 있고 컴포넌트의 개수가 많은 상황이라면 Context API를 사용하는 것이 좋다.