yarn create react-app context-tutorial
Context를 만듭니다.
//contexts/color.js
import { createContext } from "react";
const ColorContext = createContext({ color: "black" });
export default ColorContext;
Consumer라는 컴포넌트를 통해서 색상을 조회해봅니다.
//components/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;
컴포넌트의 children이 있어야할 자리에 일반 JSX 나 함수를 전달!! (Render Props)
App에 렌더링 하면
import React from "react";
import ColorBox from "./components/ColorBox";
const App = () => {
return (
<div>
<ColorBox />
</div>
);
};
export default App;
이렇게 검은색으로 나옵니다.
Provider 를 사용해서 Context의 value의 값을 변경할수 있습니다.
import React from "react";
import ColorBox from "./components/ColorBox";
import ColorContext from "./context/color";
const App = () => {
return (
<ColorContext.Provider value={{ color: "red" }}>
<div>
<ColorBox />
</div>
</ColorContext.Provider>
);
};
export default App;
provider에는 value를 꼭 명시해주어야합니다.
Context 값을 업데이트 해야하는 경우 어떻게 해야 하는지 알아보겠습니다.
??엥? Provider를 사용하면 되는거 아니였나요?? 초기값 변경은 되고 클릭했을때 값 바꾸고 이런건 안되나보다..
아래 코드는 Context와 Provider 컴포넌트로 구성되어있습니다.
Provider 컴포넌트에서 상태와 업데이트함수들을 Context로 넘겨봅니다.
// 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 { Consumer: ColorConsumer } = ColorContext;
// ColorProvider와 ColorConsumer 내보내기
export { ColorProvider, ColorConsumer };
export default ColorContext;
위 파일에서 ColorProvider 라는 컴포넌트를 새로 작성해 주었습니다.
컴포넌트는 props를 input으로 하고 UI가 어떻게 보여야 하는지 정의하는 React Element를 output으로 하는 함수입니다
그리고 그 컴포넌트에서 ColorContext.Provider를 렌더링하고 있습니다.
이 Provider의 value에는 상태는 state로 업데이트 함수는 actions로 묶어서 전달하고 있습니다. Context에서 값을 동적으로 사용할 때 반드시 묶어줄 필요는 없지만, 이렇게 state와 actions 객체를 다로따로 분리해 주면 나중에 다른 컴포넌트에서 Context의 값을 사용할 때 편합니다.
추가로 createContext를 사용할 때 기본값으로 사용할 객체도 수정했습니다. createContext의 기본값은 실제 Provider의 value에 넣는 객체의 형태와 일치시켜 주는 것이 좋습니다. 그렇게 하면 Context코드를 볼 때 내부 값이 어떻게 구성되어 있는지 파악하기도 쉽고, 실수로 Provider를 사용하지 않았을 때 리액트 애플리케이션 에러가 발생하지 않습니다.
-> 요약해보자면 Update 함수를 Context로 넘겼다
App 컴포넌트에서 ColorContext.Provider를 ColorProvider로 대체해보겟습니다.
// 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 도 마찬가지로 ColorContext.Consumer를 ColorConsumer로 변경하세요
// 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;
위 코드에서 destructuring 을 사용하면 <ColorConsumer>
태그 안에서
{({ state }) => (
<>
<div
style={{
width: "64px",
height: "64px",
background: state.color,
}}
/>
<div
style={{
width: "32px",
height: "32px",
background: state.subcolor,
}}
/>
</>
)}
이렇게 사용할수 있습니다.
이제 클릭했을 때 색상바꾸는 예제를 만들어보겟습니다.
Context의 action들을 사용하기위해!
// components/SelectColor.js
import React from "react";
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;
App 컴포넌트에서 위에 코드 렌더링
// 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;
이제 해당 SelectColors에서 마우스왼쪽, 오른쪽 클릭하면 색상 변경하도록 구현해보겠습니다.
// 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;
이제 클릭이 됩니다.
Consumer 대신 Hook 도는 static contextType 사용하는 것도 뒤에 있는데.. 그냥 스킵하겠습니다.