개요
useContext
는 컴포넌트에서 context를 읽고 구독할 수 있게 해주는 React Hook입니다.
const value = useContext(SomeContext)
Reference
useContext(SomeContext)
컴포넌트의 최상위에서 useContext
를 호출하여 context를 읽고 구독할 수 있습니다.
import { useContext } from 'react';
function MyComponent() {
const theme = useContext(ThemeContext);
// ...
}
파라미터
SomeContext
:createContext
로 생성한 context 객체. context 자체는 정보를 저장하지 않고, 어떤 정보를 제공/읽을 수 있는지 나타냅니다.
반환값
- 호출한 컴포넌트 기준으로 가장 가까운
SomeContext.Provider
의value
prop 값을 반환합니다. 만약 상위에 provider가 없다면,createContext
에서 지정한defaultValue
를 반환합니다. 반환값은 항상 최신 상태이며, context가 변경되면 해당 값을 읽는 컴포넌트가 자동으로 리렌더링됩니다.
주의사항
useContext()
는 반드시 provider가 _상위_에 있어야 하며, 같은 컴포넌트 내에서 provider를 반환해도 적용되지 않습니다.- context를 사용하는 자식들은 provider의 value가 바뀌면 자동으로 리렌더링됩니다. memo로 감싸도 context 값은 항상 최신으로 전달됩니다.
- 빌드 시스템 문제(예: symlink 등)로 provider와 consumer가 서로 다른 context 객체를 참조하면 동작하지 않습니다. 반드시 동일한 context 객체를 사용해야 합니다.
사용법
트리 깊숙이 데이터 전달하기
컴포넌트 트리의 깊은 곳까지 데이터를 전달할 때, context를 사용하면 중간 컴포넌트에서 props로 전달하지 않아도 됩니다.
import { useContext } from 'react';
function Button() {
const theme = useContext(ThemeContext);
// ...
}
context 값을 전달하려면, 상위 컴포넌트에서 provider로 감싸야 합니다.
function MyPage() {
return (
<ThemeContext.Provider value="dark">
<Form />
</ThemeContext.Provider>
);
}
function Form() {
// ... 내부에 Button 렌더링 ...
}
provider와 consumer 사이에 몇 단계의 컴포넌트가 있어도, 하위에서 useContext(ThemeContext)
를 호출하면 가장 가까운 provider의 값을 받습니다.
주의
useContext()
는 항상 _상위_에 있는 provider만 찾습니다. 같은 컴포넌트 내에서 provider를 반환해도 적용되지 않습니다.
context로 전달한 데이터 업데이트하기
context로 전달하는 값이 바뀌어야 한다면, 부모 컴포넌트에서 state로 관리하고 그 값을 provider에 전달하세요.
function MyPage() {
const [theme, setTheme] = useState('dark');
return (
<ThemeContext.Provider value={theme}>
<Form />
<Button onClick={() => setTheme('light')}>Switch to light theme</Button>
</ThemeContext.Provider>
);
}
이제 provider 내부의 모든 Button은 최신 theme 값을 받게 됩니다. setTheme으로 값을 바꾸면 하위 컴포넌트가 자동으로 리렌더링됩니다.
기본값(fallback) 지정하기
createContext(defaultValue)
로 context를 만들 때 기본값을 지정할 수 있습니다. provider가 없을 때만 이 값이 사용됩니다.
const ThemeContext = createContext('light');
트리 일부에서 context 값 덮어쓰기
provider를 중첩해서 트리의 일부에서 context 값을 덮어쓸 수 있습니다.
<ThemeContext.Provider value="dark">
...
<ThemeContext.Provider value="light">
<Footer />
</ThemeContext.Provider>
...
</ThemeContext.Provider>
이렇게 하면 Footer 내부의 Button은 "light" 값을, 바깥쪽 Button은 "dark" 값을 받게 됩니다.
객체/함수 전달 시 리렌더 최적화
context로 객체나 함수를 전달하면, 부모가 리렌더될 때마다 새로운 객체/함수가 생성되어 하위 컴포넌트가 불필요하게 리렌더링될 수 있습니다. 이럴 때는 useCallback, 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>
);
}
트러블슈팅
provider의 값을 읽지 못하는 경우
- provider가 useContext를 호출하는 컴포넌트 _상위_에 있어야 합니다.
- provider를 빼먹었거나, 트리 구조가 예상과 다를 수 있습니다. React DevTools로 계층 구조를 확인하세요.
- 빌드 시스템 문제로 provider와 consumer가 서로 다른 context 객체를 참조할 수 있습니다. 반드시 동일한 context 객체를 사용하세요.
context의 기본값과 다르게 undefined가 반환되는 경우
- provider에 value prop을 빼먹었거나, prop 이름을 잘못 썼을 수 있습니다.
// 🚩 잘못된 예시: value prop이 없음
<ThemeContext.Provider>
<Button />
</ThemeContext.Provider>
// 🚩 잘못된 예시: prop 이름이 다름
<ThemeContext.Provider theme={theme}>
<Button />
</ThemeContext.Provider>
- 올바른 예시:
<ThemeContext.Provider value={theme}>
<Button />
</ThemeContext.Provider>
- 기본값은 provider가 _아예 없을 때_만 사용됩니다. 만약
<SomeContext.Provider value={undefined}>
가 상위에 있다면, useContext는 undefined를 반환합니다.