react는 여러개의 컴포넌트로 이루어져 있고 최상위 App 컴포넌트로 부터 시작하여 트리 형태로 이루어져 있다. 일반적인 데이터 흐름은 위에서 아래로 진행되어 부모에서 자식으로 props를 전달하는 흐름이다. 그러기 위해서는 부모에서 자식으로 일일이 단계별로 전달해야하는 방법을 따라야 한다. 이로 인해 Prop Drilling이 발생한다.
💡 잠깐) Prop Drilling
props를 오로지 하위 컴포넌트로 전달하는 용도로만 쓰이는 컴포넌트들을 거치면서 React Component 트리의 한 부분에서 다른 부분으로 데이터를 전달하는 과정
- 데이터가 필요없는 중간 컴포넌트들도 데이터를 받아야하기 때문에 코드가 복잡하고 더러워진다.
- 만약 데이터를 전달하는 과정에서 잘못 전달하거나 데이터를 수정해버리면 모든 상위 컴포넌트들을 찾아니면서 에러를 해결해야한다.
이를 해결하기 위해서 React에서 Context API가 제공된다.
Context는 앱 안에서 전역적으로 사용되는 데이터들을 props로 일일이 넘기지 않아도 여러 컴포넌트들 끼리 쉽게 공유 할 수 있는 방법을 제공한다.
💡 주의) Context는 필요할 때만!
context를 사용하면 컴포넌트를 재사용하기 어려워질 수 있다. 따라서, 만약 prop drilling을 피하기 위한 목적이라면 Component Composition(합성)을 먼저 고려하는 것이 좋다.
context를 이용하면 단계마다 일일이 props를 넘겨주지 않고도 컴포넌트 트리 전체에 데이터를 제공할 수 있다(출처: 공식문서)
context API를 사용하기 위해서는 Provider , Consumer , createContext가 필요!
✏️ Context.js
import React, { createContext } from "react";
import Children from "./Children";
// AppContext 객체를 생성한다.
export const AppContext = createContext();
const Context = () => {
const user = {
name: "김채원",
job: "가수"
};
return (
<>
<AppContext.Provider value={user}> //value값에 공유할 값 저장
<div>
<Children />
</div>
</AppContext.Provider>
</>
);
};
export default App;
✏️ Children.js
import React from "react";
import { AppContext } from "./Context";
const Children = () => {
return (
<AppContext.Consumer> //consumer을 통해 context의 변화를 감시
{(user) => (
<>
<h3>AppContext에 존재하는 값의 name은 {user.name}입니다.</h3>
<h3>AppContext에 존재하는 값의 job은 {user.job}입니다.</h3>
</>
)}
</AppContext.Consumer>
);
};
export default Children;
예시 출처: https://velog.io/@jminkyoung/TIL-6-React-Hooks-useContext-%EB%9E%80
⭐️ 위와 같은 예시의 경우가 바로 context API의 예시이다. 하지만, 공유하게될 context가 많아져서 위와 같은 consumer가 계속하여 작성되면 어떨까? context의 중첩으로 코드의 가독성이 굉장히 떨어질 것이다.
✏️ 예시)
<AppContext.Provider value={user} />
<PostContext.Provider value={post} />
<AppContext.Consumer>
{user => (
<PostsContext.Consumer>
{posts => {
...
return (
...
)
}}
</PostsContext.Consumer>
)}
</AppContext.Consumer>
⭐️ 이러한 점을 useContext로 아주 깔끔하게 사용이 가능하다.
context를 생성하고 provider을 통해 값이 공유되는 것까지의 과정은 같다. 단 consumer의 사용으로 코드를 복잡하게 하지 않고, useContext로 쉽게 값을 받아와 사용할 수 있다.
import React, { useContext } from "react";
import { AppContext } from "./App";
const Children = () => {
// useContext를 이용해서 따로 불러온다.
const user = useContext(AppContext);
const post = useContext(PostContext);
return (
<>
<h3>AppContext에 존재하는 값의 name은 {user.name}입니다.</h3>
<h3>AppContext에 존재하는 값의 job은 {user.job}입니다.</h3>
<h3>PostContext에 존재하는 값의 job은 {post.job}입니다.</h3>
</>
);
};
export default Children;
👍 내가 작성한 프로젝트 내의 context API https://velog.io/@pjh1011409/%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%84-React
참조
쉬운설명: https://www.youtube.com/watch?v=LwvXVEHS638&list=PLZ5oZ2KmQEYjwhSxjB_74PoU6pmFzgVMO&index=5