Today I Learned ... react.js
🙋♂️ Reference Book
🙋 My Dev Blog
리액트를 다루는 기술 DAY 15
- context API
props
로 전달함.state
로 넣어 관리함.Context API
가 많이 개선되었음.기존에는 최상위 컴포넌트에서 여러 컴포넌트를 거쳐 props를 통해 데이터(state, setState)를 전달했지만,
Context API
를 사용하면 단 한번에 원하는 값을 받아와 사용 가능함.
src/contexts/color.js
import { createContext } from "react";
const ColorContext = createContext({ color: 'black' });
export default ColorContext;
React.createContext
함수를 사용.참고) Provider과 Consumer
데이터에 접근하고 싶은 컴포넌트를 context API의 Provider
로 묶어줘야함.
전달해줄 데이터는 value 어트리뷰트로 적어줌.
예 >
return (
<TableContext.Provider
value={{tableData: state.tableData, dispatch}}
> ... </TableContext.Provider>
);
src/components/ColorBox.js
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)
Function as a child (=Render Props)
- children이 있어야 할 자리에 JSX나 문자열이 아닌 함수를 전달해줌.
ex>
const RenderPropsSample = ({ children }) => { return <div>결과: {children(5)}</div>; };
<RenderPropsSample>{value => value * 2}</RenderPropsSample>
-> RenderPropsSample에 props.childeren으로 함수를 넘기고,
RenderPropsSample 컴포넌트에서는 children을 함수로 사용 -> children(5) 를 해서 10을 리턴.
App.js
import ColorBox from './components/ColorBox';
function App() {
return (
<div>
<ColorBox />
</div>
);
}
export default App;
background: value.color
로 가져왔기 때문에, 데이터를 잘 받아온 것을 알 수 있다.즉, Consumer로 감싸주면
내부에서 createContext()로 만든colorContext
의 데이터를 사용할 수 있다.
- 데이터에 접근하고 싶은 컴포넌트를 context API의 Provider로 묶어줘야함.
- 전달해줄 데이터는 value 어트리뷰트로 적어줌.
App.js (수정)
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;
- 기존에 createContext 함수에서 인자로 기본값인 {color: 'black'}을 넣어줬었다.
createContext
함수에서 설정한 기본값은 Provider을 사용하지 않았을 때만 사용됨.❗️ 단, Provider을 사용하려면 반드시 'value' 값을 명시해줘야 오류가 발생하지 ❌
src/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>
);
};
// ColorContext.Provider = ColorProvider (value, children 포함
// ColorContext.Consumer = ColorConsumer
const { Consumer: ColorConsumer } = ColorContext;
export { ColorProvider, ColorConsumer };
export default ColorContext;
🔺 정리
- createContext로 ColorContext의 기본값 설정
- ColorProvider, ColorConsumer을 export함.
- ColorProvider에서는
value
에 데이터를 저장- ColorContext.Consumer은 ColorConsumer임.
ColorProvider
로 수정.import ColorBox from './components/ColorBox';
import { ColorProvider } from './contexts/color';
function App() {
return (
<ColorProvider>
<div>
<ColorBox />
</div>
</ColorProvider>
);
}
export default App;
ColorConsumer
로 수정.subcolor
로)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;
value.state.color
로 수정. (객체 구조가 바뀌었으므로)참고 - value.state.color 대신
구조분해 할당으로{({ state }) => ( ... state.color) }
과 같이 사용 가능!
-> value.action 필드 사용시에도 마찬가지임.
components/SelectColors.js
const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];
const SelectColors = () => {
return (
<div>
<h2>색상을 선택하세요.</h2>
<div style={{ display: 'flex' }}>
{colors.map(color =>
(<div
key={color}
style={{
background: color,
width: '24px',
height: '24px',
cursor: 'pointer',
}}
/>
))}
</div>
<hr />
</div>
);
}
export default SelectColors;
map에서 각 div를 onClick 이벤트로는 setColor(color)을 해주면 되고,
oncontextMenu 이벤트로는 setSubcolor(color)을 해주면 된다.
🔺 정리
color.js에서 value.action을 통해 setColor, setSubcolor을 전달해주었음 (colorProvider)
-> ColorBox 컴포넌트에서는 ColorConsumer을 임포트하여 value.state.color을 가져다 씀.
-> 마찬가지로 value.action.setColor도 ColorBox 컴포넌트에서 사용 가능.
- ColorProvider는 App.js에서 쓰고, (데이터를 사용할 컴포넌트들을 감싸줌)
- ColorConsumer는 ColorBox.js에서 씀.
-> 데이터를 사용 + 변경도 함. (value.state.color을 받아오고, value.state.setColor함수도 전달받아 직접 상태변경도 가능)
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;
ColorBox에서 했던 것 처럼
<ColorConsumer></ColorConsumer>
사이에 Function as child 방식으로 함수 넣어줌. (JSX를 리턴하는 함수)
->value
값을 받아와서 value.actions.setColor과 value.actions.setSubcolor을 이용함.
import { 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;
const value = useContext(context명)
을 해주면 된다.const {state}
와 같이 해줌.<ColorConsumer>
{() => <>...</>}
</ColorConsumer>
위와 같이 Consumer사이에 Function as child 방식 (=Render Props) 을 사용하는 것 보다 훨씬 간결해졌다.
const { state } = useContext(ColorContext);
return ( <>...</> );
import { Component } from "react";
import ColorContext from "../contexts/color";
const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];
class SelectColors extends Component {
static contextType = ColorContext;
render() {
return (
<div>
<h2>색상을 선택하세요.</h2>
<div style={{ display: 'flex' }}>
{colors.map(color =>
(<div
key={color}
style={{
background: color,
width: '24px',
height: '24px',
cursor: 'pointer',
}}
/>
))}
</div>
<hr />
</div>
);
}
}
export default SelectColors;
static contextType = ColorContext;
-> this.context
에 현재 context의 value가 저장됨.
this.context.actions.setColor
이런식으로 사용하면 됨.SelectColors.js (수정)
import { Component } from "react";
import ColorContext from "../contexts/color";
const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];
class SelectColors extends Component {
static contextType = ColorContext;
onClickColor = color => {
this.context.actions.setColor(color);
}
onRightClickColor = 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.onClickColor(color)}
onContextMenu={(e) => {
e.preventDefault();
this.onRightClickColor(color);
}}
/>
))}
</div>
<hr />
</div>
);
}
}
export default SelectColors;