리액트 프로젝트에서 전역적으로 사용할 데이터가 있을때 유용한 기능이다
🤔 전역적을 사용하는 데이터는 어떻게 관리해야할까?
기존에는
⇒ 리액트는 컴포넌트 간에 데이터르 props로 전달하기에(부모 → 자식) 최상위 컴포넌트에 데이터를 저장을 한다
⇒ 하단에 있는 컴포넌트(G)가 상태값을 변경하고, 다른 하단에 있는 컴포넌트(F)가 상태 값을 업데이트 해야하면, 변경된 값이 다시 순차적으로 최상단 부터 → F까지 이어지게 되는 복잡함이 생긴다
유지 보수성이 떨어진다!
contextAPI를 이용하면 이러한 데이터 흐름이 편해진다
// color.js
import { createContext } from "react";
const ColorContext = createContext({ color: 'black' });
export default ColorContext;
//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;
// App.js
import React from 'react'
import ColorBox from './components/ColorBox'
function App() {
return (
<div>
<ColorBox />
</div>
)
}
export default App
const RenderPropsSample = ({ children }) => {
return <div>결과: {children(5)}</div>;
};
export default RenderPropsSample;
만약 위와 같은 컴포넌트가 있다면 추후 사용할 때 다음과 같이 사용할 수 있습니다.
<RenderPropsSample>{value => 2 * value}</RenderPropsSample>;
RenderPropsSample에게 children props로 파라미터에 2를 곱해서 반환하는 함수를 전달하면 해당 컴포넌트에서는 이 함수에 5를 인자로 넣어서 “결과: 10“을 렌더링합니다.
Context의 value를 변경할수 있다
import React from 'react'
import ColorBox from './components/ColorBox'
import ColorContext from './contexts/color'
function App() {
return (
<ColorContext.Provider value={{ color: 'red' }}>
<div>
<ColorBox />
</div>
</ColorContext.Provider>
)
}
export default App
지금까지는 고정 값만 사용할수 있었다.
이번에는 Context의 값을 업데이트 해야하는 경우 어떻게 해야하는지 알아보자
state의 value 값을 변경해자
// Color.js
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 ColorConsumer = ColorContext.Consumer와 같은 의미
const { Consumer: ColorConsumer } = ColorContext;
// ColorProvider와 ColorConsumer 내보내기
export { ColorProvider, ColorConsumer };
export default ColorContext;
//App.js
import React from 'react';
import ColorBox from './components/ColorBox';
import { ColorProvider } from './contexts/color';
const App = () => {
return (
<ColorProvider>
<div>
<ColorBox />
</div>
</ColorProvider>
);
};
export default App;
//ColorBox.js
import React from 'react';
import { ColorConsumer } from '../contexts/color';
const ColorBox = () => {
return (
<ColorConsumer>
{value => ( // value = {state} 로 설정하면, 비구조화 할당이된다.
<>
<div
style={{
width: '64px',
height: '64px',
background: value.state.color
}}
/>
<div
style={{
width: '32px',
height: '32px',
background: value.state.subcolor
}}
/>
</>
)}
</ColorConsumer>
);
};
export default ColorBox;
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;
즉 결론적으로 contextAPI을 이용하기 위해서는 provider로 감싸고, 그 하부에 있는 컴포넌트들은 consumner로 value or action 받아와, 사용할수 있다
하지만 앞서 context파일을 수정해서. 보기 쉽게 사용할수 있게 되었다
consumer 대신 다른 방식으로 값을 받아오자
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;
useContext으로 접근
import React from 'react';
import { ColorConsumer } from '../contexts/color';
const ColorBox = () => {
return (
<ColorConsumer>
{value => ( // value = {state} 로 설정하면, 비구조화 할당이된다.
<>
<div
style={{
width: '64px',
height: '64px',
background: value.state.color
}}
/>
<div
style={{
width: '32px',
height: '32px',
background: value.state.subcolor
}}
/>
</>
)}
</ColorConsumer>
);
};
Consumer, Reder porps 패턴으로 접근
비교해보자
함수형 컴포넌트에서 useContext를 사용하면 state값으로 바로 접근해 사용할수있다
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);
//나중에 action에 접근할게 많아면
//이렇게 간단화 시키자 const actions = this.context.actions //// 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;
static으로 다른 함수들이 contextType에 접근하게 하자, contextType = Colorcontext 이니, action 접근해서 상용하면 된다.