Context API란?
- Context API는 리액트 프로젝트에서 전역적으로 사용할 데이터가 있을 때 유용한 기능이다. 리덕스, 리액트 라우터, styled-components 등의 라이브러리는 Context API 기반으로 구현되어있다.
- 프로젝트 내에서 환경설정, 사용자 정보와 같은 전역적으로 필요한 상태를 관리해야할 때 유용하다.
- Context는 React 컴포넌트 트리 안에서 전역적(global)이라고 볼 수 있는 데이터를 공유할 수 있도록 고안된 방법이다. Context는 모든 레벨의 컴포넌트 트리에 props를 통하지 않아도 데이터를 전달할 수 있는 방법을 제공해준다.
Context API를 사용하는 이유
- 리액트 애플리케이션은 컴포넌트 간에 데이터를 props로 전달하기 때문에 컴포넌트 여기저기에서 필요한 데이터가 있을 경우 주로 최상위 컴포넌트인 App의 state에 넣어서 관리한다. 만약 뎁스가 깊은 프로젝트일 경우 루트에서 자식까지 데이터를 전달할 때 중복 코드가 발생하고, 그럴 경우 유지보수 효울성이 낮아진다.
- 하지만 context를 이용하면 단계마다 일일이 props를 넘겨주지 않고도 컴포넌트 트리 전체에 데이터를 제공할 수 있다.
- 때문에 리덕스나 MobX같은 상태관리 라이브러리를 사용하여 전역 상태관리 작업을 편하게 처리한다. 리액트 16.3v 업데이트 이후에는 Context API가 맣이 개선되었기 때문에 별도 라이브러이 없이 전역 상태를 손쉽게 관리할 수 있다.
Context API 사용하기
Context 객체 만들기
Consumer 사용하기
<MyContext.Consumer>
{value => }
</MyContext.Consumer>
- Consumer는 Context 변화를 구독하는 역할을 한다. 즉 Context에서 설정한 값을 컴포넌트 내에서 조회 때 사용하는 컴포넌트다. 이 컴포넌트를 사용하면 함수 컴포넌트안에서 context를 구독할 수 있다.
- Context.Consumer의 자식은 함수여야한다. 이 함수는 context의 현재값을 매개변수로 받고 React 노드를 반환한다. 이 함수가 받는 value 매개변수 값은 해당 context의 Provider 중 상위 트리에서 가장 가까운 Provider의 value prop과 동일하다. 상위에 Provider가 없다면 value 매개변수 값은 createContext()에 보냈던 defaultValue와 동일하다.
- Consumer 사용시 중요한 것은 Consumer 사이에 중괄호를 열어 그 안에 함수를 넣는 것이다. 이렇게 컴포넌트의 child가 있어야할 자리에 일반 JSX 혹은 문자열이 아닌 함수를 넣는 패턴을 Function as a child 혹은 Render Props라고 한다.
- 예
import React from 'react';
import ColorContext from '../context/color';
const ColorBox = () => {
return (
<ColorContext.Consumer>
{
value => (
<div style={{
width: '64px',
height: '64px',
background: value.color
}} />
)
}
</ColorContext.Consumer>
);
};
export default ColorBox;
Provide 사용하기
<MyContext.Provider value={}>
동적 Context 사용하기
- 색상선택 예제로 동적 context 사용하는 법 배우기
1. context 만들기
import React, { 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 { Consumer: ColorConsumer } = ColorContext
export { ColorProvider, ColorConsumer }
export default ColorContext;
2. Consumer 사용
import React from 'react';
import { ColorConsumer } from '../context/color';
const ColorBox = () => {
return (
<ColorConsumer>
{
({state}) => (
<>
<div style={{
width: '64px',
height: '64px',
background: state.color
}} />
<div style={{
width: '32px',
height: '32px',
background: state.subcolor
}} />
</>
)
}
</ColorConsumer>
);
};
export default ColorBox;
3. Provider 사용
import React from 'react';
import { ColorConsumer } from '../context/color';
const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];
const SelectColor = () => {
return (
<div>
<h2>Select color</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 SelectColor;
import './App.css';
import ColorBox from './components/ColorBox';
import { ColorProvider } from './context/color';
import SelectColor from './components/SelectColor';
function App() {
return (
<ColorProvider>
<div className="App">
<SelectColor />
<ColorBox />
</div>
</ColorProvider>
);
}
export default App;
Consumer 대체 문법
함수형 컴포넌트
const value = useContext(Context);
- 함수형 컴포넌트에서 useContext Hook을 사용하면 Context를 편하게 사용할 수 있다.
import React, {useContext} from 'react';
import ColorContext from '../context/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;
클래스형 컴포넌트
- 클래스형 컴포넌트에서는
static contextType
을 정의하면 Context를 쉽게 사용할 수 있다. 클래스 상단에 static contextType
를 정의하면 this.context
로 Context에 접근할 수 있다.
- 단 클래스형 컴포넌트에서는 한 클래스에서 하나의 Context 밖에 사용하지 못한다.
- 예
import React, { Component } from 'react';
import ColorContext from '../contexts/color';
const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];
class SelectColors extends Component {
static contextType = ColorContext;
handleSetColor = color => {
this.context.actions.setColor(color);
};
handleSetSubcolor = subcolor => {
this.context.actions.setSubcolor(subcolor);
};
render() {
return (
<div>
<h2>색상을 선택하세요.</h2>
<div style={{ display: 'flex' }}>
{colors.map(color => (
<div
key={color}
style={{
background: color,
width: '24px',
height: '24px',
cursor: 'pointer'
}}
onClick={() => this.handleSetColor(color)}
onContextMenu={e => {
e.preventDefault();
this.handleSetSubcolor(color);
}}
/>
))}
</div>
<hr />
</div>
);
}
}
export default SelectColors;