Context
- Context는 리액트 컴포넌트간에 어떠한 값을 공유할 수 있게 해주는 기능
- Context API는 Redux나 Mobx 같은 상태관리 라이브러리들의 기반이 됨
- 전역적으로 사용할 데이터가 있을 때 유용한 기술
- 예를 들면 사용자 로그인 정보, 애플리케이션 환경 설정, 테마 등
- 주로 Context는 전역적(global)으로 필요한 값을 다룰 때 사용하는데 꼭 전역적일 필요는 없음
→ 단순히 "리액트 컴포넌트에서 Props가 아닌 또 다른 방식으로 컴포넌트 간에 값을 전달하는 방법이다" 라고 접근을 하는 것이 좋음
컴포넌트에게 데이터를 전달할 때 Props로만 데이터를 전달하게 되면 깊숙히 위치한 컴포넌트에 데이터를 전달해야 하는 경우, 여러 컴포넌트를 거쳐 연달아 Props를 설정해주어야 하기 때문에 불편하고 실수할 가능성이 높아짐(그림 15-2)
→ 이러한 문제들을 Context API를 사용해 Context를 만들어 단 한번에 원하는 값을 받아와 해결
Context API 사용법
// contexts/color.js
import { createContext } from 'react';
const ColorContext = createContext({ color : 'black' });
export default ColorContext;
- Context 는 리액트 패키지에서 createContext 라는 함수를 사용
- 파라미터에는 해당 Context의 기본 상태를 지정함
// 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;
- ColorBox라는 컴포넌트를 만들어 ColorContext 안에 들어있는 색상을 보여줌
- 색상을 props로 받아오는 것이 아니라 ColorContext 안에 들어 있는 Consumer라는 컴포넌트를 통해 색상을 조회
- Consumer 사이에 중괄호를 넣어 그 안에 함수를 넣어줌
- Function as a child, 혹은 Render Props라고 한다
- 컴포넌트의 children이 있어야 할 자리에 일반 JSX 혹은 문자열이 아닌 함수를 전달하는 것
더보기
Render Props 패턴
const RenderPropsSample = ({ children }) => {
return <div>결과 : {children(5)}</div>;
};
export default RenderPropsSample;
위와 같은 컴포넌트가 있다면 추후 사용할 때 다음과 같이 사용
<RenderPropsSample>{value => 2 * value}</RenderPropsSample>;
RenderPropsSample에게 children props로 파라미터에 2를 곱해서 반환하는 함수를 전달하면 해당 컴포넌트에서는 이 함수를 5에 인자로 넣어서 "결과 : 10"을 렌더링함
// App.js
import ColorBox from './components/ColorBox';
const App = () => {
return (
<div>
<ColorBox />
</div>
);
};
export default App;
Provider를 사용하면 Context의 value를 변경할 수 있음
// App.js
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를 사용하지 않았을 때만 사용된다. 만약 Provider는 사용했는데 value를 명시하지 않았따면, 이 기본값을 사용하지 않기 때문에 오류가 발생
동적 Context
- Context의 value에는 함수를 전달해 줄 수 도 있음
- Context 값을 업데이트 하는 경우 사용
- Provider 사용시 초기값 변경은 가능하나 클릭 이벤트나 값을 변경하는 이벤트는 불가능함
// contexts/color.js
// - Context와 Provider 컴포넌트로 구성됨
import { createContext, useState } from 'react';
// - createContext의 기본값은 실제 Provider의 value에 넣는 객체의 형태와 일치시키는게 좋음
const ColorContext = createContext({
state : { color : 'black', subcolor : 'red' },
actions : {
setColor : () => {},
setSubcolor : () => {}
}
});
const ColorProvider = ({ children }) => {
const [color, setColor] = useState('black');
const [subcolor, setSubcolor] = useState ('red');
// Provider의 상태는 state로 업데이트 함수는 actions로 묶어서 전달
// Context에서 값을 동적으로 사용할 때 반드시 묶어줄 필요는 없지만,
// state와 actions 객체를 따로 분리해주면 다른 컴포넌트에서 Context의 값을 사용할 때 편리
const value = {
state : {color, subcolor},
actions : {setColor, setSubcolor}
};
return (
<!-- ColorContext.Provider를 렌더링
-->
<ColorContext.Provider value = {value}> {children} </ColorContext.Provider>
);
};
<!-- const ColorConsumer = ColorContext.Consumer와 같은 의미 -->
const { Consumer : ColorConsumer } = ColorContext;
<!-- ColorProvider와 ColorConsumer 내보내기 -->
export { ColorProvider, ColorConsumer };
export default ColorContext;
→ 요약해보자면 Update 함수를 Context로 넘겼다
// App.js
// App 컴포넌트에서 ColorContext.Provider를 ColorProvider로 대체함
import ColorBox from './components/ColorBox';
import { ColorProvider } from './contexts/color';
const App = () => {
return (
<ColorProvider>
<div>
< ColorBox />
</div>
</ColorProvider>
);
};
export default App;
// components/ColorBox.js
// ColorContext.Consumer를 ColorConsumer로 변경하세요
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;
value 조회하는 것을 생략할 수 있음
{({ state }) => (
<>
<div
style={{
width: "64px",
height: "64px",
background: state.color,
}}
/>
<div
style={{
width: "32px",
height: "32px",
background: state.subcolor,
}}
/>
</>
)}
// components/SelectColor.js
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;
// App.js
import React from "react";
import ColorBox from "./components/ColorBox";
import SelectColors from "./components/SelectColors";
import { ColorProvider } from "./contexts/color";
const App = () => {
return (
<ColorProvider>
<div>
<SelectColors />
<ColorBox />
</div>
</ColorProvider>
);
};
export default App;
useContext Hook 사용
- useContext Hook은 함수형 컴포넌트에서만 사용가능
static contextType 사용
- 클래스형 컴포넌트에서 Context를 좀 더 쉽게 사용하기 위해 static context Type을 정의
- 기존 selectColors의 컴포넌트를 클래스형으로 변경
- useContext를 사용하는게 더 권장
'React' 카테고리의 다른 글
[React] 리덕스를 사용해 상태 관리 (0) | 2023.04.06 |
---|---|
[React] 리덕스 라이브러리 (0) | 2023.04.05 |
[React] 라우팅 (0) | 2023.03.30 |
[React] immer를 사용해 불변성 유지 (0) | 2023.03.29 |
[React] 컴포넌트 스타일링 (0) | 2023.03.23 |