[React] useContext로 데이터에 쉽게 접근하기
💚 Context
컴포넌트 트리를 통해 컴포넌트에 곧바로 데이터를 전달하는 방식
props를 사용하지 않아도 곧바로 데이터를 전달할 수 있어 코드가 깔끔해지고
데이터를 한곳에서 관리하므로 디버깅에 유리하다.
💡 언제 context를 사용하는게 좋을까?
여러 컴포넌트에서 계속해서 접근이 일어날 수 있는 데이터들이 있는 경우 사용하는 것이 좋다.
Provider의 모든 하위 컴포넌트가 얼마나 깊이 위치해 있는지 관계없이 context의 데이터를 읽을 수 있어
props를 사용하지 않고도 데이터에 접근할 수 있다.
context를 사용하기 전에 고려할 점
context는 다양한 레벨에 걸쳐진 많은 컴포넌트에게 데이터를 전달하기 위한 용도로
컴포넌트와 context가 연동되면 재사용성이 떨어지게 된다. (컴포넌트 간의 의존성이 높아질 수 있기 때문!)
따라서 다른 레벨의 많은 컴포넌트가 데이터를 필요로 하는 경우가 아니라면 props를 통해 데이터를 전달하는 방식이 더 적합하다.
context를 사용하기에 적합한 데이터
- 현재 지역 정보
- UI 테마
- 캐싱된 데이터
Context API
1. React.createContext( )
context를 생성하기 위한 함수
const MyContext = React.createContext(기본값);
기본값을 파라미터로 전달하면 컨텍스트 객체가 만들어진다.
이때 기본값은 Provider를 찾지 못한 경우에 쓰이는 값!
Provider로 undefined를 값으로 보내는 경우 구독 컴포넌트들은 기본값을 읽지 못한다.
2. Context.Provider
Provider: 데이터를 제공해주는 컴포넌트
Provider는 context를 구독하는 컴포넌트들에게 context의 변화를 알리는 역할을 하며 value prop을 받아 하위에 있는 컴포넌트에게 전달한다.
<MyContext.Provider value={값} >
<SideBar />
<MainPage />
</MyContext.Provider>
Provider 컴포넌트로 하위 컴포넌트들을 감싸주면 모든 하위 컴포넌트들이 해당 컨텍스트의 데이터에 접근할 수 있다.
❗️ 값을 전달받을 수 있는 컴포넌트의 수에 제한은 없다.
또한 여러개의 Provider 컴포넌트를 중첩시켜 사용할 수 있다.
3. Class.ContextType <- 현재는 class 컴포넌트 사용이 거의 없어 잘 사용하지 않음
Provider 하위에 있는 클래스 컴포넌트에서 context의 데이터에 접근하기 위해 사용한다.
MyClass.contextType = MyContext;
이 방법은 단 하나의 context만 구독할 수 있다.
4. Context.Consumer
Provider의 하위 컴포넌트로 context의 데이터를 구독하는 컴포넌트이다. (데이터를 소비한다는 의미!)
consumer 컴포넌트는 context의 값이 변경되면 value prop을 위한 객체가 매번 생성되기 때문에 리렌더링이 발생한다.
<MyContext.Consumer>
{value => <p>{value}</p>
</MyContext.Consumer>
하나의 Provider 컴포넌트는 여러개의 consumer 컴포넌트와 연결될 수 있다.
이때 상위 레벨에 매칭되는 Provider가 없을 경우 기본값이 사용된다.
5. Context.displayName
크롬의 리액트 개발자 도구에서 표시되는 context 객체의 이름이다.
MyContext.displayName = "MyDisplayName";
⭐️ 6. useContext( ) ⭐️
React 16.8 이상 버전에서 context 값을 사용하는 훅
React.createContext 함수 호출로 생성된 context 객체를 인자로 받아 현재 context의 값을 리턴한다.
const value = useContext(MyContext);
context의 값이 변경되면 변경된 값과 함께 useContext 훅을 사용하는 컴포넌트가 리렌더링되기 때문에
컴포넌트의 렌더링이 꽤 무거운 작업일 경우에는 별도의 최적화 처리를 해주는게 좋다.
💚 Context API 활용 예시
개인 프로젝트 중 '숫자 야구' 프로젝트에서 context API를 활용해보았다.
왜나고 물으신다면....
사실 리액트 강의를 듣고 써보고 싶었기 때문!!
이라고 하면 면접 광탈! 일까요,,,,,,,,힝
숫자 야구는 많은 페이지나 컴포넌트가 요구되는 프로젝트는 아니었기 때문에
기존에 작성하던 코드만 사용하지 않고 새로운 무언가를 더 시도해보고 싶었다.
export const ResultContext = React.createContext();
export const GameOverContext = React.createContext();
export const RandomNumberContext = React.createContext();
필요한 context 객체를 생성한다.
function App() {
const [openRule, setOpenRule] = useState(false);
const [gameOver, setGameOver] = useState(false);
const [randomNum, setRandomNum] = useState(getRandomNumbers());
const [result, setResult] = useState([]);
const handleGameRule = useCallback(() => setOpenRule(!openRule), [openRule]);
...
}
최상위 컴포넌트에서 Provider로 넘겨줄 state와 함수를 생성한다.
function App() {
...
return (
<RandomNumberContext.Provider
value={{ randomNum, setRandomNum, getRandomNumbers }}
>
<GameOverContext.Provider value={{ gameOver, setGameOver }}>
<ResultContext.Provider value={{ result, setResult }}>
<BrowserRouter>
<AppWrapper>
<Header handleGameRule={handleGameRule} />
{openRule && <GameRule handleGameRule={handleGameRule} />}
{gameOver && <GameOver />}
<Routes>
<Route path="/" element={<StartGame />} />
<Route path="/game" element={<Game />} />
</Routes>
</AppWrapper>
</BrowserRouter>
</ResultContext.Provider>
</GameOverContext.Provider>
</RandomNumberContext.Provider>
);
}
Provider 컴포넌트로 하위 컴포넌트를 감싸주면 완성!
Provider의 순서는 상관 없고 가장 바깥에 작성하면 된다.
const Game = () => {
const { result, setResult } = useContext(ResultContext);
const { gameOver, setGameOver } = useContext(GameOverContext);
const { randomNum, setRandomNum, getRandomNumbers } =
useContext(RandomNumberContext);
return (
<>
...
<ScoreBoard />
</>
);
};
하위 컴포넌트에서 context에 접근할 때는 useContext를 사용했다.
각 context의 Provider로 전달한 데이터를 필요에 맞게 꺼내온다.
여기서 Game 컴포넌트의 하위 컴포넌트인 ScoreBoard 컴포넌트에서도 context에 접근할 수 있다.
const ScoreBoard = () => {
const { result } = useContext(ResultContext);
...
};
💬 사용 후기
숫자 야구 프로젝트에서 활용하고 난 뒤 드는 생각은
"굳이 사용을 안해도 되지 않았을까?"
props로 전달하는 것과 코드양도 비슷하고, 오히려 더 복잡해보이기도 한다.
아마 적합한 프로젝트가 아니여서 일지도 모르겠지만
props를 직접 전달하지 않고 상태 관리 라이브러리 없이 데이터를 사용할 수 있는 방법을 알게 되었으니
더 다양하게 코드를 작성할 수 있게 된 것 같아 뿌-듯하다✌️
여기까지 오신 분들은 숫자야구 한 판 하고 가쎄요!
https://numberbaseball.web.app/
숫자야구
숫자 야구 게임하러 오세유~
numberbaseball.web.app