앞서 공부한 내용은 고정적인 값만 사용할 수 있었던 예제임.
이번엔 Context 값을 업데이트해야 할 경우엔 어떻게 해야하는지 알아보자!
Context의 value에는 무조건 상태 값만 있어야 하는 것은 아님!!! 함수를 전달해 줄 수도 있음.
동적 Context 사용할 수 있다는 이야기!
import { useState, createContext } from "react";
// == 새 Context를 만들 때 ==
// => createContext 함수를 사용함.
// => 파라미터에는 해당 Context의 기본 상태를 지정함.
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;
ColorProvider라는 컴포넌트를 새로 작성함.
그리고 그 컴포넌트에서는 ColorContext.Provider를 렌더링하고 있음. 이 Provider의 value에는 상태는 state로, 업데이트 함수는 actions로 묶어서 전달하고 있음.
Context에서 값을 동적으로 사용할 때 반드시 묶어 줄 필요는 없지만, 이렇게 state와 actions 객체를 분리하면 다른 컴포넌트에서 Context의 값을 사용할 때 편함.
추가로 createContext를 사용할 때 기본값으로 사용할 객체도 수정함.
createContext의 기본값은 실제 Provider의 value에 넣는 객체의 형태와 일치시키는 게 좋음.
그렇게 하면 Context 코드를 볼 때 내부 값이 어떻게 구성되어 있는지 파악하기 쉽고, 실수로 Provider를 사용하지 않았을 때 리액트 애플리케이션에서 에러가 발생하지 않음.
👉 기존 ColorBox.jsx 수정
ColorBox도 마찬가지로 ColorContext.Consumer를 ColorConsumer로 변경한다.
사용할 value의 형태도 바뀌었으니 이에 따른 변화도 반영시켜보자!
import React from "react";
import { ColorConsumer } from "../contexts/color";
// import ColorContext 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;
import React from "react";
import "./App.css";
import ColorBox from "./components/ColorBox";
import { ColorProvider } from "./contexts/color";
function App() {
return (
// <ColorContext.Provider value={{ state: { color: "red", subColor: "blue" } }}>
// <div>
// <ColorBox />
// </div>
// </ColorContext.Provider>
<ColorProvider>
<div>
<ColorBox />
</div>
</ColorProvider>
);
}
export default App;
실행하면... 이런 결과가 나온다.

여기서 더 나아가서 색상 선택 컴포넌트를 만들어보자!
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>
))}
</div>
<hr />
</div>
);
};
export default SelectColors;
App 컴포넌트에서 ColorBox 위에 렌더링해보자!

과연... 결과는??!

(+) 추가로 SelectColors에서 마우스 왼쪽 버튼을 클릭하면 큰 정사각형의 색상이 변경되고, 마우스 오른쪽 버튼을 클릭하면 작은 정사각형의 색상이 변경되게 해보자!
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={() => {
console.log("왼쪽 마우스 클릭:", color); // 왼쪽 클릭 시 찍히는 값
actions.setColor(color); // 왼쪽 클릭 시 큰 색상 변경
}}
onContextMenu={(e) => {
e.preventDefault(); // 마우스 오른쪽 버튼 클릭 시 기본 메뉴 방지
console.log("오른쪽 마우스 클릭:", color); // 오른쪽 클릭 시 찍히는 값
actions.setSubColor(color); // 오른쪽 클릭 시 작은 색상 변경
}}
/>
))}
</div>
)}
</ColorConsumer>
<hr />
</div>
);
};
export default SelectColors;
잘 바뀌는지 확인해보자!


