Context 와 렌더링 이슈
Context 와 렌더링 이슈
React Document 참고(https://react.dev/reference/react#context-hooks)
Context 란?
props 전달 없이 멀리 있는 부모로부터 정보를 받을 수 있는 컴포넌트
특징
1. 정보를 전달하지만 컨텍스트 그 자체는 상태 관리 툴이 아니기 때문에 useState, useReducer 와 같은 상태 관리 훅과 함께 사용된다.
2. 같은 컨텍스트 라면 오버라이드가 가능하다. (링크 두번째 예제)
오버라이드?
- 타깃 컴포넌트에서 가까운 순으로 순차적으로 타깃 컨텍스틀를 탐색하기 때문에 가능하다
- 장점: nested 한 컴포넌트를 계층별로 정리하기 좋다.
- 단점: 유지보수하기 어렵다. 한 눈에 어떤 계층의 컨텍스트를 구독하는지 알 수가 없어서 헷갈릴 것 같다.
사용
1. Context.Provider : 컨텍스트 value 를 제공해주는 컴포넌트
2. Context.Consumer: context value 를 read 하는 컴포넌트 (요즘은 useContext 를 대신 사용한다.)
UseContext 란?
컨텍스트를 구독하게끔 하는 훅
- 구독을 한 컨텍스트의 value 값이 변경될 때 해당 컴포넌트는 리렌더링이 일어나게 된다.- useContext 를 사용했으나 해당 컨텍스트의 Provider 를 찾지 못하는 경우 미리 설정 해놓은 defaultValue 를 사용하게 된다.
import { useContext } from 'react';
function MyComponent() {
const theme = useContext(ThemeContext);
// ...
CreateContext 란?
컨텍스트를 생성하는 훅
- context 의 defaultValue : 한 번 설정된 이후 변하지 않는 값, useContext 사용시 Provider 를 찾지 못하는 경우 fallback 값으로 사용됨
import { createContext } from 'react';
const ThemeContext = createContext('light');
Context 사용하기 전 고려해보기
꼭 필요한가?
- passing props, 컴포넌트 합성과 같은 방법으로 대체할 수 있는지 고민해보자
- 위 두가지 방법이 context 보다 명확하다
보통 언제 사용할까?
- themeing, current account, Routing, Managing state (너무 먼 컴포넌트에서 state 를 제어하려고 할 때)
조심해야할 것
- useContext 와 Context.Provider 를 같은 컴포넌트 안에서 사용하지 말 것
- 프로바이더로 감싸는걸 까먹지 말 것
렌더 이슈
같은 컨텍스트 Provider 의 Child 이지만 구독은 하나만 하고 있는 경우
- useContext 를 사용해 구독중이지 않은 컴포넌트도 theme 값이 변하는 경우 같이 리렌더가 됨
- InDependentChild 에 React.memo 를 사용해주면 불필요한 리렌더를 방지 할 수 있음
(But, 리렌더에 의해 성능문제가 발생할 정도의 컴포넌트가 아니라면 굳이 memo 를 할 필요가 없다고 본다.)
const ThemeContext = createContext('light');
export default function MyApp() {
const [theme, setTheme] = useState('light');
return (
<>
<ThemeContext.Provider value={theme}>
<DependentChild /> // 컨텍스트 구독 중
<IndependentChild /> // 컨텍스트 구독 중 아님
</ThemeContext.Provider>
<Button onClick={() => {
setTheme(theme === 'dark' ? 'light' : 'dark');
}}>
Toggle theme
</Button>
</>
)
}
같은 컨텍스트 Provider 의 Child 이고 둘다 구독 중이고 각각 value 내 특정 프로퍼티 값을 사용하는 경우
- 두개의 Child 모두 하나의 컨텍스트를 구독 중임
- 컴포넌트 a 는 value.a 를 컴포넌트 b 는 value.b 값을 사용한다고 했을 때, a 값에만 상태 변화가 있어도 두개의 컴포넌트 모두 리렌더가 되게 됨
- 대안: Context 를 쪼개서 사용
컨텍스트의 value 에 참조타입을 넣는 경우
- 참조타입은 새로 생성되는 경우 값이 동일하다고 해도 참조가 달라지기 때문에 다른 참조라고 판단이 되어 실제로 변경되지 않아도 리렌더가 일어나게 됨 (링크 참고)
- 대안: useMemo 나 useCallback 을 사용하여 메모이제이션을 해주자