컴포넌트가 프롭스(props)를 전달하지 않고 먼 부모(parent) 컴포넌트로부터 정보를 받을 수 있도록 하는 것을 "컨텍스트(Context)"라고 합니다. 예를 들어, 앱의 최상위 컴포넌트는 얼마나 깊은 위치에 있던 하위 모든 컴포넌트에 대해 현재 UI 테마를 전달할 수 있습니다.
useContext
는 컨텍스트를 읽고 구독(subscribe)합니다.function Button() {
const theme = useContext(ThemeContext);
// ...
useContext
는 컴포넌트에서 컨텍스트를 읽고 구독(subscribe)할 수 있도록 하는 React Hook입니다.
const value = useContext(SomeContext)
useContext(SomeContext)
컨텍스트를 읽고 구독하려면 컴포넌트의 최상위 수준에서 useContext
를 호출하세요.
import { useContext } from 'react';
function MyComponent() {
const theme = useContext(ThemeContext);
// ...
SomeContext
: 이전에 createContext
로 생성한 컨텍스트입니다. 컨텍스트 자체는 정보를 보유하지 않으며, 컴포넌트에서 제공하거나 읽을 수 있는 정보의 유형을 나타냅니다.useContext
는 호출하는 컴포넌트의 컨텍스트 값을 반환합니다. 이 값은 호출 컴포넌트의 가장 가까운 조상인 SomeContext.Provider
에서 전달된 값으로 결정됩니다. 만약 해당 프로바이더가 없는 경우, 해당 컨텍스트에 대해 createContext
에 전달한 defaultValue
값이 반환됩니다. 반환된 값은 항상 최신 상태입니다. React는 컨텍스트를 읽는 컴포넌트가 해당 값이 변경되면 자동으로 다시 렌더링됩니다.
useContext()
를 호출하면 해당 컴포넌트에서 반환된 프로바이더에 영향을받지 않습니다. 즉, useContext()
를 호출하는 컴포넌트보다 <Context.Provider>
가 먼저 선언되어 있어야 합니다.Object.is
비교를 통해 비교합니다. memo를 사용하여 재 렌더링을 건너 뛰어도 하위 컴포넌트는 새로운 컨텍스트 값을 수신합니다.SomeContext
가 동일한 객체여야 하며 ===
비교를 통해 확인할 수 있어야 합니다.컨텍스트를 읽고 구독하려면 컴포넌트의 최상위 수준에서 useContext
를 호출하세요.
import { useContext } from 'react';
function Button() {
const theme = useContext(ThemeContext);
// ...
useContext
는 전달한 컨텍스트에 대한 컨텍스트 값을 반환합니다. 컨텍스트 값을 결정하기 위해 React는 컴포넌트 트리를 탐색하고 해당 컨텍스트에 대한 가장 가까운 컨텍스트 프로바이더를 찾습니다.
Button 컴포넌트에 컨텍스트를 전달하려면, 해당 컨텍스트 프로바이더를 감싸거나 부모 컴포넌트 중 하나를 감싸세요.
예시)
function MyPage() {
return (
<ThemeContext.Provider value="dark">
<Form />
</ThemeContext.Provider>
);
}
function Form() {
// ... renders buttons inside ...
}
프로바이더와 Button
사이에 몇 개의 컴포넌트가 있더라도 상관없습니다. Form
내부의 Button
에서 useContext(ThemeContext)
를 호출하면, "dark"
를 값으로 받게 됩니다.
❗️Pitfall
useContext()
는 항상 호출한 컴포넌트의 상위 프로바이더 중 가장 가까운 것을 찾습니다. 즉, useContext()
를 호출하는 컴포넌트에서 프로바이더를 사용하는 경우, 해당 프로바이더는 고려하지 않습니다.
보통 컨텍스트가 시간이 지남에 따라 변경되도록 원합니다. 컨텍스트를 업데이트하려면, 상태(state)와 함께 사용합니다. 부모 컴포넌트에서 상태 변수를 선언하고 현재 상태를 프로바이더의 컨텍스트 값으로 전달하세요.
function MyPage() {
const [theme, setTheme] = useState('dark');
return (
<ThemeContext.Provider value={theme}>
<Form />
<Button onClick={() => {
setTheme('light');
}}>
Switch to light theme
</Button>
</ThemeContext.Provider>
);
}
이제 프로바이더 내부의 모든 Button이 현재 theme
값을 받게됩니다. setTheme
을 호출하여 전달하는 theme
값을 업데이트하면, 모든 Button
컴포넌트가 새로운 'light'
값으로 다시 렌더링됩니다.
React가 부모 트리에서 해당 컨텍스트의 프로바이더를 찾을 수 없는 경우, useContext()
로 반환된 컨텍스트 값은 해당 컨텍스트를 생성할 때 지정한 기본값과 동일합니다
예시)
const ThemeContext = createContext(null);
기본값은 항상 변경되지 않습니다. 컨텍스트를 업데이트하려면 위에서 설명한대로 상태와 함께 사용하세요.
일반적으로 null 대신 더 의미있는 값을 기본값으로 사용할 수 있습니다.
예시)
const ThemeContext = createContext('light');
이렇게 하면 실수로 프로바이더를 빼먹어도 컴포넌트가 오류를 내지 않습니다. 또한, 테스트 환경에서 많은 프로바이더를 설정하지 않고도 컴포넌트가 잘 작동하도록 할 수 있습니다.
트리의 일부에 대해 컨텍스트를 재정의하려면, 해당 부분을 다른 값으로 갖는 프로바이더로 감싸면 됩니다.
<ThemeContext.Provider value="dark">
...
<ThemeContext.Provider value="light">
<Footer />
</ThemeContext.Provider>
...
</ThemeContext.Provider>
필요한 만큼 프로바이더를 중첩하고 재정의할 수 있습니다.
객체 및 함수를 포함하여 컨텍스트를 통해 모든 값을 전달할 수 있습니다.
function MyApp() {
const [currentUser, setCurrentUser] = useState(null);
function login(response) {
storeCredentials(response.credentials);
setCurrentUser(response.user);
}
return (
<AuthContext.Provider value={{ currentUser, login }}>
<Page />
</AuthContext.Provider>
);
}
위 예시에서 컨텍스트 값은 함수를 포함하는 JavaScript 객체입니다. MyApp
이 다시 렌더링 될 때마다(예: 라우트 업데이트) 다른 함수를 가리키는 다른 객체가됩니다. 그러므로 React는 useContext(AuthContext)
를 호출하는 트리의 깊숙한 모든 컴포넌트를 다시 렌더링해야 합니다.
작은 앱에서는 이것이 문제가 되지 않을 수 있습니다. 그러나 currentUser
와 같은 기본 데이터가 변경되지 않은 경우, 다시 렌더링할 필요가 없습니다. React가 이 사실을 활용할 수 있도록 useCallback
으로 login
함수를 감싸고 useMemo
로 객체 생성을 감싸면 됩니다.
import { useCallback, useMemo } from 'react';
function MyApp() {
const [currentUser, setCurrentUser] = useState(null);
const login = useCallback((response) => {
storeCredentials(response.credentials);
setCurrentUser(response.user);
}, []);
const contextValue = useMemo(() => ({
currentUser,
login
}), [currentUser, login]);
return (
<AuthContext.Provider value={contextValue}>
<Page />
</AuthContext.Provider>
);
}
이 변경으로 인해 MyApp
이 다시 렌더링되더라도, useContext(AuthContext)
를 호출하는 컴포넌트는 currentUser
가 변경되지 않는 한 다시 렌더링할 필요가 없습니다.
출처:
https://react.dev/reference/react/useContext#passing-data-deeply-into-the-tree