프로젝트를 해보면서 Context, redux 등이 아직 모자라 복습의 필요성을 느꼇다. 먼저 정적인 상태에서 사용법을 다시 한번 익혀보자.
Context 기본 사용법
//contexts/color.js
import { createContext } from "react";
//기본 상태 지정
const ConlorContext = createContext({ color: "black" });
export default ConlorContext;
//components/ColorBox.js
import React from "react";
import ConlorContext from "../contexts/color";
function ColorBox() {
return (
<ConlorContext.Consumer>
{(value) => (
<div
style=
/>
)}
</ConlorContext.Consumer>
);
}
export default ColorBox;
위와 같이 Consumer
컴포넌트를 통해 색상의 조회가 가능하다.
Consumer
는 context 변화를 구독하는 컴포넌트 인데 함수 컴포넌트 안에서 context 를 읽기 위해 쓸 수 있다.
또 Consumer
컴포넌트의 children 은 JSX 나 문자열이 아닌 함수여야 하는데, 저 때 value 값은 해당 context 의 Provider 중 가장 가까운 Provider 의 value 를 참조한다. 만약 상위에 Provider 가 없다면 값은 createContext() 에서 보낸 기본값과 동일하다.
참고로 Consumer
와 같이 내부에 함수를 넣어주는 패턴을 Function as a child, 또는 Render Props 라고 한다.
Provider
만들어 놓은 Context 로 App.js 에서 렌더링되는 컴포넌트를 감싸주고 이때 Provider 를 사용해주면 Context 의 value 값을 변경할 수 있다.
//App.js
import ColorBox from "./components/ColorBox";
import ColorContext from "./contexts/color";
function App() {
return (
<ColorContext.Provider value=>
<ColorBox></ColorBox>
</ColorContext.Provider>
);
}
export default App;
만약 Provider 를 사용할 때는 value 값을 반드시 명시해줘야 한다는 것을 잊지말자. 기존에 Context 에서 지정해놓은 기본값은 Provider 가 사용되지 않을 때만 사용된다.
동적 Context 사용
Context 의 value 로 상태값만이 아닌 함수도 전달해 줄 수 있다.
//contexts/color.js
import { createContext, useState } from "react";
const ColorContext = createContext({
state: { color: "black", subColor: "red" },
action: {
setColor: () => {},
setSubColor: () => {},
},
});
const ColorProvider = ({ children }) => {
const [color, setColor] = useState("black");
const [subColor, setSubColor] = useState("red");
//따로 분리해주는 것이 다른 컴포넌트에서 사용하기 편함
const value = {
state: { color, subColor },
action: { setColor, setSubColor },
};
return (
<ColorContext.Provider value={value}>{children}</ColorContext.Provider>
);
};
//const ColorConsumner = ColorContext.Counsumer 과 같음
const { Consumer: ColorConsumner } = ColorContext;
//만든것들 내보내기
export { ColorProvider, ColorConsumner };
export default ColorContext;
//App.js
import ColorBox from "./components/ColorBox";
import ColorContext, { ColorProvider } from "./contexts/color";
function App() {
return (
<ColorProvider>
<ColorBox></ColorBox>
</ColorProvider>
);
}
export default App;
//components/ColorBox.js
import React from "react";
import ColorContext, { ColorConsumner } from "../contexts/color";
function ColorBox() {
return (
<ColorConsumner>
{(value) => (
<>
<div
style=
></div>
<div
style=
></div>
</>
)}
</ColorConsumner>
);
}
export default ColorBox;
검은색 박스와 빨간색 박스가 정상적으로 나온다. 이때 createContext 의 기본값은 실제 Provider 의 value 값과 일치하는 것이 좋다. 그래야 Context 코드를 볼 때 내부값이 어떻게 구성되어 있는지 파악하기가 쉽고 실수로 Provider 을 사용하지 않은 경우 에러가 발생하지 않는다.
색상 선택 컴포넌트 만들기
위의 Context 에서 action 에 넣은 함수를 호출하는 컴포넌트를 만들어보자.
//components/SelectColor.js
import React from "react";
import { ColorConsumner } from "../contexts/color";
const colors = ["red", "blue", "yellow", "pink", "green", "indigo"];
function SelectColor() {
return (
<div style=>
<h2>색상을 선택하세요</h2>
<ColorConsumner>
{({ action }) => (
<div style=>
{colors.map((color) => (
<div
key={color}
style=
onClick={() => action.setColor(color)}
onContextMenu={(e) => {
e.preventDefault(); //우클릭시 메뉴 뜨는 것 무시
action.setSubColor(color);
}}
></div>
))}
</div>
)}
</ColorConsumner>
</div>
);
}
export default SelectColor;
//App.js
import ColorBox from "./components/ColorBox";
import SelectColor from "./components/SelectColor";
import { ColorProvider } from "./contexts/color";
function App() {
return (
<ColorProvider>
<SelectColor></SelectColor>
<ColorBox></ColorBox>
</ColorProvider>
);
}
export default App;
색상을 선택하는 div 들에서 action 객체를 사용하기 위해 ColorConsumer 로 감싸주고 왼쪽 클릭이냐 오른쪽클릭이냐에 따라 setColor, setSubColor 를 줌으로써 좌클릭시 위의 박스, 우클릭시 아래의 박스 색상이 변하도록 해줬다.
useContext
이전에 공부했던 것과 같이 useContext 를 사용하면 Consumer 보다 더욱 편하게 값을 사용할 수 있다. 다만 함수형 컴포넌트에서만 사용이 가능하다.
//components/ColorBox.js
import React, { useContext } from "react";
import ColorContext, { ColorConsumner } from "../contexts/color";
function ColorBox() {
const { state } = useContext(ColorContext);
return (
<>
<div
style=
></div>
<div
style=
></div>
</>
);
}
export default ColorBox;