팀장님께 컴포넌트 설계에 대해 조언을 구했을때 context를 사용하는건 어떻겠냐고 말씀을 주셔서 context를 공부하게 되었다.
그러나 여러 블로그를 봐도 원하는 예제가 없어 직접 블로그에 정리하려고 쓴다. 나는 react + typescript + 함수 표현식으로 사용했다. 따라서 이 기술 스택을 사용하지 않는 독자(?)들은 자신의 기술 스택대로 바꿔보는것도 좋은 방식일것같다!
많은 사람이 개념에 대해 설명했으니 짧게 설명하고 넘어가겠다. 😎
context란 일일이 props를 넘겨주지 않고 컴포넌트 트리 전체에 데이터를 제공하는 방식을 말한다. 즉 많은 컴포넌트가 값을 공유하도록 할 수 있다.
단순 text를 props로 넘기려고 거친 comp(= components)가 ParentsComp 기준 두 단계이다. 만약 이 과정에서 comp가 몇 차례 더 추가된다면 끊임없이 props를 만들어야한다.. 그렇게 된다면 너무 끔찍할 것 같지 않은가?(ts는 props type도 알려줘야해서 손이 더 아플듯.. 😅)
const ParentsComp = ({ value }: { value: string }) => {
return <ChildComp value={value} />;
};
const ChildComp = ({ value }: { value: string }) => {
return <TextComp value={value} />;
};
const TextComp = ({ value }: { value: string }) => {
return <div>{value}</div>;
};
function App() {
return <ParentsComp value="Hello Jetom🤗" />;
}
이렇듯 일일이 props를 내려주기 힘들기 때문에 context를 사용하는것! (상태관리 라이브러리는 논외로 치자 😎)
우선 나는 UpdateTextValueDataContext, TextValueDataContext라는 context를 두 가지 만들었는데 그 이유는 다음과 같다.
이렇게 만들고 나면 상황에 따라 context를 사용 할 수 있어 편하다!
import { ReactNode, createContext, useState } from "react";
export const TextValueDataContext = createContext<string | null>(null);
export const UpdateTextValueDataContext = createContext<
(payload: string) => void
>(() => {});
const TextValueContext = ({ children }: { children: ReactNode }) => {
const [text, setText] = useState<string>("Hyeri😎");
return (
<UpdateTextValueDataContext.Provider
value={(value) => {
setText(value);
}}
>
<TextValueDataContext.Provider value={text}>
{children}
</TextValueDataContext.Provider>
</UpdateTextValueDataContext.Provider>
);
};
export default TextValueContext;
이 코드에서 눈 여겨볼것은 두 가지이다.
첫 째는 createContext, 둘 째는 Provider!
createContext<string | null>(null)
라고 쓰면 초기 value는 null이 된다!
Provider는 context를 구독하는 컴포넌트들에게 변화를 알리는 역할이다. Provider로 감싸는 모든 컴포넌트는 리렌더링 된다.(리렌더링 되는 점이 치명적인 단점으로 꼽히기도 한다.)
이제 마지막 단계인 context가 적용된 comp다! 바로 예시로 들어가겠다.
// App.tsx
import ResultComp from "./ResultComp";
import TextValueContext from "./context";
function App() {
return (
<TextValueContext>
<ResultComp />
</TextValueContext>
);
}
export default App;
App.tsx에 TextValueContext를 한번 감싸야한다! 꼭 App에 해야하는것이 아니라 정확히는 부모 컴포넌트에 context를 넣어야한다. 동일한 파일에서 context로 감싸고 구독하면 원하는 값이 절대 나오지 않는다.(경험담이다...😅) 최소한 한번 감싸져야한다..
다음은 사용처에서의 사용법이다.
import { useContext } from "react";
import { TextValueDataContext } from "./context";
const ResultComp = () => {
const textContext = useContext(TextValueDataContext);
return <div>Hello {textContext}</div>;
};
export default ResultComp;
useContext는 context를 사용하겠다고 알리는 hook이라 생각하면 되고, 사용 방법은 다음과 같다
// textContext라고 변수를 만들었다.
// useContext를 사용해 TextValueDataContext를 가져온다.
//TextValueDataContext는 초기값을 설정한 createContext다
const textContext = useContext(TextValueDataContext);
결과물은 다음과 같다!
💬 hello는 기존 comp의 text고 Hyeri는 context의 값을 가져온것!
대망의 update! 사실 별거 없어서 안쓰려다가... 이왕 쓴거 다쓰자! 심정으로 쓴다.
(UpdateTextValueDataContext는 너무 기니까 update라 부르겠다)
update는 payload를 받았는데, 이것으로 TextValueContext의 text 값을 바꾸려는 추진력을 얻으려한것이다! 그래서 TextValueDataContext와 살짝 초기 코드가 다르다.(차이를 못 느낀 분은 잠시 다시 보시고 오는걸 추천!)
결과물
import { useContext } from "react";
import { TextValueDataContext, UpdateTextValueDataContext } from "./context";
const ResultComp = () => {
const textContext = useContext(TextValueDataContext);
const updateTextContext = useContext(UpdateTextValueDataContext);
const handleChangeText = (payload: string) => {
updateTextContext(payload);
};
return (
<div
style={{ cursor: "pointer" }}
onClick={() => {
handleChangeText("Jetom 🐾");
}}
>
Hello {textContext}
</div>
);
};
export default ResultComp;
코드를 살펴보면 div에 onClick시 handleChangeText를해 text를 바꾸는 간단한 로직이고 이때 updateTextContext라는 UpdateTextValueDataContext를 사용하는 변수를 만들어 그 안에 바꾸고싶은 text를 넣게했다. (payload는 곧 text를 바꾼다는 것) 이제 div를 클릭하면 hyeri -> jetom으로 textContext value가 바뀌는것을 볼 수 있다.
이렇게 context에 대해 간단하게(?) 알아보았다. (Provider말고 Consumer라는 것도 있던데 이건 단순히 구독하는 역할이니 넘어갔다!)
📚 참고 사이트
https://ko.legacy.reactjs.org/docs/context.html
https://velog.io/@velopert/react-context-tutorial#context-%EB%9E%80
🧑 참고인(?)
팀장님