리액트에서 Props Drilling을 해결할 수 있는 방법 중 하나다. useContext를 사용해 어떤 자식 컴포넌트일지라도 부모 컴포넌트에 있는 데이터를 사용할 수 있다.
이 2가지 해법이 적절하지 않는 상황에서 context 사용을 고려할 수 있다. 컴포넌트 트리 최상단에서 아주 멀리 떨어진 일부 컴포넌트에서 특정 데이터가 필요할 때 context가 도움이 될 수 있다.
Before : imageSize가 props로 계속 전달되고 있는 상황
// App.js
import { useState } from 'react';
import { places } from './data.js';
import { getImageUrl } from './utils.js';
export default function App() {
const [isLarge, setIsLarge] = useState(false);
const imageSize = isLarge ? 150 : 100;
return (
<>
<label>
<input
type="checkbox"
checked={isLarge}
onChange={e => {
setIsLarge(e.target.checked);
}}
/>
Use large images
</label>
<hr />
<List imageSize={imageSize} />
</>
)
}
function List({ imageSize }) {
const listItems = places.map(place =>
<li key={place.id}>
<Place
place={place}
imageSize={imageSize}
/>
</li>
);
return <ul>{listItems}</ul>;
}
function Place({ place, imageSize }) {
return (
<>
<PlaceImage
place={place}
imageSize={imageSize}
/>
<p>
<b>{place.name}</b>
{': ' + place.description}
</p>
</>
);
}
function PlaceImage({ place, imageSize }) {
return (
<img
src={getImageUrl(place)}
alt={place.name}
width={imageSize}
height={imageSize}
/>
);
}
After : context를 이용해 props 제거
1) imageSize props를 대체할 context 생성
// Context.js
import { createContext } from "react";
export const ImageSizeContext = createContext();
2) 프로바이더로 list를 감싸준 뒤, PlaceImage 컴포넌트에서 useContext를 사용해 context 사용하고 기존 props 제거
// App.js
import { useState, useContext } from "react";
import { places } from "./data.js";
import { getImageUrl } from "./utils.js";
import { ImageSizeContext } from "./Context.js";
export default function App() {
const [isLarge, setIsLarge] = useState(false);
const imageSize = isLarge ? 150 : 100;
return (
<ImageSizeContext.Provider value={imageSize}>
<label>
<input
type="checkbox"
checked={isLarge}
onChange={(e) => {
setIsLarge(e.target.checked);
}}
/>
Use large images
</label>
<hr />
<List />
</ImageSizeContext.Provider>
);
}
function List() {
const listItems = places.map((place) => (
<li key={place.id}>
<Place place={place} />
</li>
));
return <ul>{listItems}</ul>;
}
function Place({ place }) {
return (
<>
<PlaceImage place={place} />
<p>
<b>{place.name}</b>
{": " + place.description}
</p>
</>
);
}
function PlaceImage({ place }) {
const imageSize = useContext(ImageSizeContext);
return (
<img
src={getImageUrl(place)}
alt={place.name}
width={imageSize}
height={imageSize}
/>
);
}
useContext로 유저 로그인 추적을 할 수 있구나! 이전 프로젝트에서는 전역 상태로 추적했었는데, 기회가 되면 다음에 써봐야겠다.