[학습 목표]
1. react context의 필요성과 필수 개념에 대해 알게 돼요.
2. useContext를 실습하게 돼요.
우리는 일반적으로 부모컴포넌트 → 자식 컴포넌트로 데이터를 전달해 줄 때 어떻게 했었나요? 그렇죠.
props 를 사용했습니다. 그러나 부모 → 자식 → 그 자식 → 그자식의 자식 이렇게 너무 깊어지게 되면 무슨 현상이 일어난다고 했었나요? 바로 prop drilling 현상이 일어나요.
prop drilling의 문제점은

출처 : https://www.copycat.dev/blog/react-context/
그래서 등장한 것이 바로 react context API라는 것이구요. useContext hook을 통해 우리는 쉽게 전역 데이터를 관리할 수 있게 되었습니다.
createContext : context 생성Consumer : context 변화 감지Provider : context 전달(to 하위 컴포넌트)구조는 다음과 같아요.

App.jsx
import "./App.css";
import GrandFather from "./components/GrandFather";
export function App() {
return <GrandFather />;
}
export default App;
GrandFather.jsx
import React from "react";
import Father from "./Father";
function GrandFather() {
const houseName = "스파르타";
const pocketMoney = 10000;
return <Father houseName={houseName} pocketMoney={pocketMoney} />;
}
export default GrandFather;
Father.jsx
import React from "react";
import Child from "./Child";
function Father({ houseName, pocketMoney }) {
return <Child houseName={houseName} pocketMoney={pocketMoney} />;
}
export default Father;
Child.jsx
import React from "react";
function Child({ houseName, pocketMoney }) {
const stressedWord = {
color: "red",
fontWeight: "900",
};
return (
<div>
나는 이 집안의 막내에요.
<br />
할아버지가 우리 집 이름은 <span style={stressedWord}>{houseName}</span>
라고 하셨어요.
<br />
게다가 용돈도 <span style={stressedWord}>{pocketMoney}</span>원만큼이나
주셨답니다.
</div>
);
}
export default Child;
결과 화면

GrandFather 컴포넌트는 Child 컴포넌트에게 houseName과 pocketMoney를 전달해주기 위해 Father 컴포넌트를 거칠 수 밖에 없었네요. 단적인 예시였지만 중간 컴포넌트가 100개라면 엄청나게 비효율이겠죠. 자, 이제 useContext hook을 적용해봅시다.
#1. context > FamilyContext.js 생성

import { createContext } from "react";
// 여기서 null이 의미하는 것은 무엇일까요?
export const FamilyContext = createContext(null);
null의 의미
createContext 함수는 Context 객체를 생성하는데, 여기서 null은 Context의 기본값(default value)을 나타냅니다.
이 기본값은 해당 Context를 Provider로 감싸지 않은 상황에서 해당 Context를 사용할 때 사용되는 값입니다. 예를 들어, 해당 Context를 사용하는 컴포넌트가 Provider로 감싸져 있지 않은 경우에 기본값으로 사용됩니다.
이 컨텍스트의 기본값이 null로 설정되었기 때문에, Provider가 제공되지 않을 때 해당 Context를 사용하는 컴포넌트는 null을 기본값으로 사용하게 됩니다. 이후에 Provider로 해당 Context를 감싸면 해당 값이 Provider에서 제공하는 값으로 대체됩니다.
#2. GrandFather.jsx 수정
import React from "react";
import Father from "./Father";
import { FamilyContext } from "../context/FamilyContext";
function GrandFather() {
const houseName = "스파르타";
const pocketMoney = 10000;
return (
<FamilyContext.Provider value={{ houseName, pocketMoney }}>
<Father />
</FamilyContext.Provider>
);
}
export default GrandFather;
#3. Father.jsx 수정(props를 제거해요!)
import React from "react";
import Child from "./Child";
function Father() {
return <Child />;
}
export default Father;
#4. Child.jsx 수정
import React, { useContext } from "react";
import { FamilyContext } from "../context/FamilyContext";
function Child({ houseName, pocketMoney }) {
const stressedWord = {
color: "red",
fontWeight: "900",
};
const data = useContext(FamilyContext);
console.log("data", data);
return (
<div>
나는 이 집안의 막내에요.
<br />
할아버지가 우리 집 이름은 <span style={stressedWord}>{data.houseName}</span>
라고 하셨어요.
<br />
게다가 용돈도 <span style={stressedWord}>{data.pocketMoney}</span>원만큼이나
주셨답니다.
</div>
);
}
export default Child;
자, Child.jsx에서 console.log를 찍어보면 어떻게 나올까요?

오…! GrandFather → Context(중앙 관리소) → Child 순서로 잘 전달이 됐군요!
이제 이 object를 이용해서 뿌려보죠.
<span style={stressedWord}>{data.houseName}</span>
<span style={stressedWord}>{data.pocketMoney}</span>

useContext를 사용할 때, Provider에서 제공한 value가 달라진다면 useContext를 사용하고 있는 모든 컴포넌트가 리렌더링 됩니다. 따라서 value 부분을 항상 신경써줘야 해요!
이후에 배우게 될 메모이제이션이 그 키가 되겠네요