React Context

Taemin Jang·2024년 3월 6일
0

Props drilling

중첩된 여러 계층의 컴포넌트에게 props를 전달해주는 것을 의미한다.

이는 해당 props가 필요하지 않은 컴포넌트들도 전달 받을 수 있으며, 중첩된 여러 컴포넌트가 많을수록 어떤 문제가 발생했을 때 해당 props가 어디서부터 왔는지 추적하기가 쉽지 않다.

// App.jsx
function App() {
  return (
    <div className="App">
      <div className="card">
        <GrandParent theme="light" />
      </div>
    </div>
  );
}

// GrandParent.jsx
function GrandParent({ theme }) {
  return <Parent theme={theme} />;
}

// Parent.jsx
function Parent({ theme }) {
  return <Child theme={theme} />;
}

// Child.jsx
function Child({ theme }) {
  return <div>theme is {theme}</div>;
}

React Context

이러한 문제를 해결하는 방법 중 하나로 React Context가 있다.

React Context는 상위 컴포넌트에서 하위 컴포넌트에게 일일이 props를 전달하지 않고 필요한 데이터를 쉽게 사용할 수 있게 해준다.

어떤 데이터를 Context로 사용하면 좋을까?

주로 앱의 모든 컴포넌트에서 사용할 수 있는 데이터를 Context로 사용하면 유용하다.

  • 현재 로그인한 유저 정보
  • 테마 데이터 (다크 모드 or 라이트 모드)
  • 지역 or 언어
  • 주로 자주 업데이트할 필요가 없는 데이터 등

리액트 Context는 컴포넌트를 위한 전역 변수처럼 사용되지만, 컴포넌트 전체 상태를 관리하기 위해 만들어진 것이 아니라 데이터를 전역으로 공유해 쉽게 사용하기 위해 만들어진 것이다. (하지만 꼭 전역으로 사용할 필요는 없다.)

사용법

// App.jsx
import { createContext } from 'react';

export const ThemeContext  = createContext('light'); // 라이트모드를 기본으로 하는 Theme context 생성

function App() {
  return (
    <div className="App">
      <div className="card">
        <ThemeContext.Provider value="dark">
          // 다크 모드로 변경한 값을 모든 하위 컴포넌트에서 읽을 수 있다.
          <GrandParent />
        </ThemeContext.Provider>
      </div>
    </div>
  );
}

// GrandParent.jsx
function GrandParent() {
  return <Parent />;
}

// Parent.jsx
function Parent() {
  return <Child />;
}

// Child.jsx
import { useContext } from 'react';
import { ThemeContext } from '../App';

function Child() {
  const value = useContext(ThemeContext); // context value
  return <div>theme is {value}</div>;
}

Context 사용하지 않고 개선하기

Context를 사용하면 컴포넌트를 재사용하기가 어려워지므로 꼭 필요할 때 써야한다.

그렇기 때문에 Context를 사용하지 않고 개선할 수 있다면 최대한 사용하지 않는 것이 중요하다.

만약 여러 레벨에 걸쳐 props 넘기는 것을 대체하기 위해 context를 사용하기 보다 컴포넌트 합성이 더 간단한 해결책일 수 있다.

컴포넌트 합성이란?

// App.jsx
function App() {
  return (
    <div className="App">
      <div className="card">
        <GrandParent name='tony' theme='light' />
      </div>
    </div>
  );
}

// GrandParent.jsx
function GrandParent(props) {
  const {name, theme} = props;
  const userTheme = <div>{name} is {theme}</div>;
  return <Parent userTheme={userTheme} />;
}

// Parent.jsx
function Parent({ userTheme }) {
  return <Child userTheme={userTheme} />;
}

// Child.jsx
function Child({ userTheme }) {
  return userTheme;
}

이렇게 컴포넌트 합성을 사용하면 name과 theme props를 쓰고 있다는 것을 아는 컴포넌트는 GrandParent 컴포넌트 밖에 없고 나머지 컴포넌트들은 해당 props에 대해 변경이 되어도 신경쓰지 않아도 된다.

또는 children을 통해 똑같이 구현할 수 있다.

// App.jsx
function App() {
  return (
    <div className="App">
      <div className="card">
        <GrandParent>
          <Parent>
            <Child name='tony' theme="light" />
          </Parent>
        </GrandParent>
      </div>
    </div>
  );
}

// GrandParent.jsx
function GrandParent({ children }) {
  return children;
}

// Parent.jsx
function Parent({ children }) {
  return children;
}

// Child.jsx
function Child({ name, theme }) {
  return <div>{name} is {theme}</div>;
}

주의사항

context를 잘못 사용하면 provider로 감싼 컴포넌트들이 모두 리렌더링 발생할 수 있다

참고

profile
하루하루 공부한 내용 기록하기

0개의 댓글