React에서 제공하는 내장 API
컴포넌트들에게 동일한 Context(맥락)을 전달하는데 사용할 수 있다.
리액트에서 데이터를 전달하는 기본 원칙은 단방향성
즉, 부모 컴포넌트에서 자식 컴포넌트 방향으로만 데이터를 전달할 수 있다는 의미
➡ 애플리케이션의 안전성을 높이고 흐름을 단순화하는데 유용하지만 때때로 너무 많은 단계를 거쳐서 자식 컴포넌트에 데이터를 전달해야 한다는 문제점을 야기
➡ Props를 사용하지 않아도 특정 값이 필요한 컴포넌트끼리 쉽게 값을 공유할 수 있게 한다.
➡ 주로 프로젝트에서 전역 상태를 관리할 때 많이 사용 한다.
기본 값을 설정하고 싶다면, createContext 함수에 인자로 기본 값을 넣어주시면 된다.
const MyContext = createContext();
const MyContext = createContext('default value');
만약, 기본 값을 보여주지 않고 아예 오류를 띄워서 개발자가 고치도록 명시를 하고 싶다면
const MyContext = createContext();
function useMyContext() {
const value = useContext(MyContext);
if (value === undefined) {
throw new Error('useMyContext should be used within MyContext.Provider');
}
}
context 객체를 만들면 ~.Provider 컴포넌트와 ~.Consumer라는 컴포넌트가 만들어진다.
Provider
Context 안에 있는 값을 사용할 컴포넌트들을 감싸주는 용도로 사용한다.
value
Context를 통해 여러 컴포넌트에서 공유할 수 있는 값
즉, 컴포넌트간에 공유하고자 하는 값을 value 라는 Props로 설정하면 자식 컴포넌트들에서 해당 값에 바로 접근을 할 수 있습니다.
function App() {
return (
<NavigationContainer>
<LogContext.Provider value="Hello World">
<FeedsStack />
</LogContext.Provider>
</NavigationContainer>
);
}
export default App;
➡ 이렇게 Provider 컴포넌트를 사용하면 이 컴포넌트 내부에 선언된 모든 컴포넌트에서 Context안의 값을 사용할 수 있다.
function FeedsStack() {
return (
<View>
<LogContext.Consumer>
{(value) => <Text>{value}</Text>}
</LogContext.Consumer>
</View>
)
}
export default FeedsStack;
원하는 컴포넌트에서 useContext
라는 Hook을 사용하여 Context에 넣은 값에 바로 접근할 수 있다.
const MyContext = createContext();
const value = useContext(MyContext);
➡ 각 컴포넌트에서 원하는 값을 useContext
로 가져오는 방식
➡ value 값이 바뀔 경우, useContext
를 사용한 모든 컴포넌트들이 리렌더링
// 예시
import { createContext, useContext } from 'react';
const MyContext = createContext();
function App() {
return (
<MyContext.Provider value="Hello World">
<AwesomeComponent />
</MyContext.Provider>
);
}
function AwesomeComponent() {
return (
<div>
<FirstComponent />
<SecondComponent />
<ThirdComponent />
</div>
);
}
function FirstComponent() {
const value = useContext(MyContext);
return <div>First Component says: "{value}"</div>;
} // First Component says: Hello World
function SecondComponent() {
const value = useContext(MyContext);
return <div>Second Component says: "{value}"</div>;
} // Second Component says: Hello World
function ThirdComponent() {
const value = useContext(MyContext);
return <div>Third Component says: "{value}"</div>;
} // Third Component says: Hello World
export default App;
Context는 전역 상태 관리를 할 수 있는 수단일 뿐이고, 상태 관리 라이브러리는 상태 관리를 더욱 편하고, 효율적으로 할 수 있게 해주는 기능들을 제공해주는 도구
예를 들어, Redux의 경우에는 액션과 리듀서라는 개념을 사용하여 상태 업데이트 로직을 컴포넌트 밖으로 분리 할 수 있게 해주며, 상태가 업데이트 될 때 실제로 의존하는 값이 바뀔 때만 컴포넌트가 리렌더링 되도록 최적화를 해준다. 만약 Context를 쓴다면, 각기 다른 상태마다 새롭게 Context를 만들어주어야 하는데, 이 과정을 생략할 수 있기에 편리하다.
MobX의 경우엔 Redux와 마찬가지로 상태 업데이트 로직을 컴포넌트 밖으로 분리할 수 있게 해주고, 함수형 리액티브 프로그래밍 방식을 도입하여 mutable한 상태가 리액트에서도 잘 보여지게 해주고 상태 업데이트 로직을 더욱 편하게 작성할 수 있게 해주며 최적화도 잘 해준다.
Recoil, Jotai, Zustand의 경우엔 Context를 일일이 만드는 과정을 생략하고 Hook 기반으로 아주 편하게 전역 상태 관리를 할 수 있게 해주죠. 최적화 기능 또한 덤으로 딸려온다.
즉, 전역 상태 라이브러리는 결국 상태 관리를 조금 더 쉽게 하기 위해서 사용하는 것이며 취향에 따라 선택해서 사용하면 되는 것