[React Docs] useContext

lyshine·2023년 6월 6일
0

React

목록 보기
1/6
post-thumbnail

useContext란

컴포넌트에서 context를 읽고 구독할 수 있게 해주는 리액트 훅이다.

useContext(SomeContext)

const value = useContext(SomeContext);
// 매개변수 SomeContext : 이전에 createContext로 생성한 context
// return : 호출하는 컴포넌트에 대한 context 값을 반환한다. context가 변경되면 context를 읽는 컴포넌트를 리렌더링한다.
import { useContext } from 'react';

function MyComponent() {
	// 컴포넌트 최상위 레벨에서 useContext를 이용해 context를 읽고 구독한다.
  const theme = useContext(ThemeContext);
  // ...
  • 컴포넌트의 useContext() 호출은 동일한 컴포넌트에서 반환된 해당 <Context.Provider>는 반드시 useContext() 호출을 수행하는 컴포넌트의 에 있어야 합니다.
  • 변경된 value를 받는 provider부터 시작해서 해당 context를 사용하는 자식들에 대해서까지 전부 자동으로 리렌더링
    • [memo](https://react-ko.dev/reference/react/memo)로 리렌더링을 건너뛰어도 새로운 context 값을 받는 자식들을 막지는 못한다.

사용방법

컴포넌트 최상위 레벨에서 호출한다.

useContext는 전달한 context에 대한 context값을 반환하고 위에서 가장 가까운 context Provider를 찾는다.

context를 Button에 전달하려면, 해당 버튼이나 상위 컴포넌트에 해당 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 ...
	// Form 내부의 Button이 useContext(ThemeContext)를 호출하면
	// "dark"를 값으로 받는다.
	return (
    <Panel title="Welcome">
      <Button>Sign up</Button>
      <Button>Log in</Button>
    </Panel>
  );
}
  • 전체코드
    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>
      );
    }

context를 통해 전달된 데이터 업데이트

  • context를 업데이트하려면 state를 사용해야 한다. 현재 state를 context값으로 provider에 전달
function MyPage() {
  const [theme, setTheme] = useState('dark');
  return (
		//theme을 context value로 provider에 전달
    <ThemeContext.Provider value={theme}>
      <Form />
      <Button onClick={() => {
				//전달한 value={theme} 를 업데이트할때는 setTheme을 호출하여 버튼 컴포넌트 리렌더링
        setTheme('light');
      }}>
        Switch to light theme
      </Button>
    </ThemeContext.Provider>
  );
}

트리 일부에 대한 context 재정의하기

트리의 일부분을 다른 값의 Provider로 감싸주면 해당 부분에 대한 context 재정의가 가능하다.

  • 필요한만큼 provider를 중첩하고 재정의가 가능
<ThemeContext.Provider value="dark">
  ...
	{/*Footer 안쪽의 버튼은 다른 context값(light)를 받는다.*/}
  <ThemeContext.Provider value="light">
    <Footer />
  </ThemeContext.Provider>
  ...
</ThemeContext.Provider>

객체 및 함수 전달시 리렌더링 최적화

  • context value로는 객체와 함수를 포함해 모든 값들을 전달할 수 있다.
  • 함수로 값을 전달하면, 컴포넌트가 라우트 업데이트같이 리렌더링할때마다 다른 함수를 가리키는 다른 객체가 되므로 ⇒ useContext를 호출하는 모든 컴포넌트도 리렌더링 된다. ⇒ 불필요한 리렌더링 발생
    • ⇒ 성능 최적화를 위해 value로 전달할 함수는 useCallback으로 감싸고, 객체 생성은 useMemo로 감싸준다.
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>
  );
}
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이 리렌더링해야 하는 경우에도 currentUser가 변경되지 않는 한useContext(AuthProvider)를 호출하는 컴포넌트는 리렌더링할 필요가 없다.

0개의 댓글