본문 바로가기
프로그래밍/React

[React] useContext

by YuminK 2023. 11. 29.

useContext

useContext is a React Hook that lets you read and subscribe to context from your component.

 

const value = useContext(SomeContext)

 

useContext는 넘긴 context 값을 반환한다. 컨텍스트 값을 결정하기 위해 리액트는 컴포넌트 트리를 검색하고 가장 가까운 컨텍스트 프로바이더를 찾는다. 컴포넌트에 Provider로 감싸거나 부모 컴포넌트 중에 하나를 감싸라. (매칭되는 컨텍스트 프로바이더로)

 

import { useContext } from 'react';

 

function Button() {

  const theme = useContext(ThemeContext);

  // ...

 

function MyPage() {

  return (

    <ThemeContext.Provider value="dark">

      <Form />

    </ThemeContext.Provider>

  );

}

 

function Form() {

  // ... renders buttons inside ...

}

 

useCotext()는 항상 호출하는 컴포넌트 위에서 가장 가까운 공급자를 찾는다. 또한 useContext()를 호출하는 컴포넌트 내부에 있는 프로바이더는 고려하지 않고 상단을 찾는다. 

 

자주 컨텍스트 값을 변경하고 싶을텐데, 이럴 때는 state와 같이 사용하면 된다. 부모 컴포넌트에 내부에 state를 선언하고 context 값을 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>

  );

 

이러면 프로바이더 내부에 존재하는 버튼은 현재 theme 값을 받게된다. 또한 setTheme을 처리하여 값이 변경되면 버튼 컴포넌트들은 다시 그려질 것이다.

 

import { createContext, useContext, useState } from 'react';

 

const ThemeContext = createContext(null);

 

export default function MyApp() {

  const [theme, setTheme] = useState('light');

  return (

    <ThemeContext.Provider value={theme}>

      <Form />

      <label>

        <input

          type="checkbox"

          checked={theme === 'dark'}

          onChange={(e) => {

            setTheme(e.target.checked ? 'dark' : 'light')

          }}

        />

        Use dark mode

      </label>

    </ThemeContext.Provider>

  )

}

 

function Form({ children }) {

  return (

    <Panel title="Welcome">

      <Button>Sign up</Button>

      <Button>Log in</Button>

    </Panel>

  );

}

 

function Panel({ title, children }) {

  const theme = useContext(ThemeContext);

  const className = 'panel-' + theme;

  return (

    <section className={className}>

      <h1>{title}</h1>

      {children}

    </section>

  )

}

 

function Button({ children }) {

  const theme = useContext(ThemeContext);

  const className = 'button-' + theme;

  return (

    <button className={className}>

      {children}

    </button>

  );

}

 

createContext를 이용하여 제공하고 싶은 정보(Context)를 생성하고 <~Context.Provider value={data}> 해당 값을 제공하는 Provider를 선언한다. 그리고 내부 컴포넌트에서 useContext값을 이용하여 처리하면 된다. 결국 상단에서 제어하는 useState값으로 제어해주면서, Provider의 value값을 변경하고 변경이 일어났을 때 해당 컨텍스트를 사용하고 있던 컴포넌트를 다시 그린다. 

 

값을 넘겨주고 싶은데 props로 하나씩 넘기고 싶지는 않고, 조부모에서 자식 관계에 해당하는 컴포넌트에게 데이터를 전달하고 싶은 경우에 사용하면 좋을 것 같다. 전역 변수 같은 느낌인데 listener 개념도 붙은 느낌? 

 

만약 Provider를 찾지 못한 경우, 컨텍스트 생성시 넘겼던 기본 값이 반환된다고 한다. 프로바이더를 2개 제공하면, 해당 위젯으로부터 가까운 프로바이더가 선택된다. (override 개념이 존재한다) 

 

useContext 최적화

 

다음과 같은 상황에서 Provider에 제공된 Object는 매번 생성된다. 

 

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>

 );

}

 

이를 방지하기 위해 login 함수를 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>

 );

}

 

이러면 MyApp부분이 다시 그려지길 원할지라도, useContext를 호출하는 컴포넌트는 다시 그려지지 않을 것이다. (currentUser가 변경되지 않았다면)

 

1. 값이 나오지 않는 경우, 프로바이더의 위치가 잘못되었을 수 있다. 외부에 선언하라.

2. 잘못된 위치에 값을 넣었거나, 컴포넌트 wrapping을 잊었을 지도 모른다.

3. 빌드툴에 문제가 발생했을 가능성이 있다. 

 

프로바이더에는 값을 넘겨야 한다. 또한 value값을 넘겨야 한다. 매칭되는 프로바이더가 없는 경우에만 createContext의 초기값이 사용된다는 것을 인지해라. 

 

https://react.dev/reference/react/useContext

'프로그래밍 > React' 카테고리의 다른 글

[React] useReducer  (0) 2023.11.30
[React] createContext  (0) 2023.11.29
[React] forwardRef  (0) 2023.11.29
[React] memo  (1) 2023.11.29
[React] useMemo  (0) 2023.11.29

댓글