[React] Context API

Bewell·2023년 12월 23일
0
post-thumbnail

이론



React context란?

리액트 context는 앱에서 props를 사용하지 않고 필요한 데이터를 넘겨주며 사용할 수 있다. 다시 말해 컴포넌트들이 데이터를 더 쉽게 공유할 수 있도록 해준다.




React context를 사용해야 할 때

context는 앱의 모든 컴포넌트에서 사용할 수 있는 데이터를 전달할 때 유용하다
예를들면

  • 테마 데이터 (라이트모드 & 다크모드)
  • 사용자 데이터
  • 로케일(지역, 언어) 데이터

context 데이터는 자주 업데이트할 필요가 적은 데이터를 사용한다. 왜냐면 context는 전체적인 상태 관리를 위해 만들어진 시스템이 아니고, 데이터를 쉽게 사용하기 위해 만들어졌다.

context 데이터는 리액트 컴포넌트를 위한 전역 변수와 같다고 생각하면 된다.




React context가 해결해주는 문제

Props Drilling문제를 해결해준다.




React context 사용 방법

  1. createContext메서드를 사용해 context를 생성한다
  2. 생성된 context를 가지고 context provider로 컴포넌트 트리를 감싼다
  3. value prop을 사용해 context provider에 원하는 값을 입력한다



실전



useContext는 컴포넌트에서 context를 읽고 구독할 수 있게 해주는 react hook이다

const value = useContext(someContext)

주의사항

  • React는 변경된 value를 받는 provider부터 시작해서 해당 context를 사용하는 자식들에 대해서까지 전부 자동으로 리렌더링한다. 이전 값과 다음 값은 Object.is로 비교한다. memo로 리렌더링을 건너뛰어도 새로운 context값을 수신하는 자식들은 막지 못한다



사용법

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

const ThemeContext = createContext(null);	//	context 생성

export default function MyApp() {
  const [theme, setTheme] = useState('light');	//	업데이트가 필요한 context일 경우에는 state변수로 관리할 수 있다.
  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);	//	context 호출
  const className = 'panel-' + theme;
  return (
    <section className={className}>
      <h1>{title}</h1>
      {children}
    </section>
  )
}

function Button({ children }) {
  const theme = useContext(ThemeContext);	//	context 호출
  const className = 'button-' + theme;
  return (
    <button className={className}>
      {children}
    </button>
  );
}



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

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>
  );
}

여기서 context값은 두 개의 프로퍼티를 가진 Js객체이며, 그 중 하나는 함수이다. MyApp이 리렌더링할 때마다(예: 라우트 업데이트), 이것은 다른함수를 가르키는, 다른객체가 될 것이므로 react는 useContext(AuthContext)를 호출하는 트리 깊숙한 곳의 모든 컴포넌트도 리렌더링해야 한다.

이러한 문제는 소규모 앱에서는 문제가 되지 않지만, currentUser와 같은 기초 데이터가 변경되지 않았다면 리렌더링할 필요가 없다. react가 이 사실을 활용할 수 있도록 login 함수를 useCallback으로 감싸고 객체 생성은 useMemo로 감싸면 된다. 이것은 성능 최적화를 위한 것이다

import { useCallback, useMemo } from 'react';

function MyApp() {
  const [currentUser, setCurrentUser] = useState(null);

  const login = useCallback((response) => {	//	useCallback적용
    storeCredentials(response.credentials);
    setCurrentUser(response.user);
  }, []);

  const contextValue = useMemo(() => ({	//	useMemo 적용
    currentUser,
    login
  }), [currentUser, login]);

  return (
    <AuthContext.Provider value={contextValue}>
      <Page />
    </AuthContext.Provider>
  );
}

이 변경으로 인해 MyApp이 리렌더링해야 하는 경우에도 currentUser가 변경되지 않는 한 useContext(AuthProvider)를 호출하는 컴포넌트는 리렌더링할 필요가 없다






참고링크1
참고링크2

0개의 댓글