공부/React

Context 와 렌더링 이슈

nr2p 2023. 8. 24. 14:27
반응형

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 을 사용하여 메모이제이션을 해주자

 

 

반응형