Context

숭이·2022년 3월 19일
0

context란?

단계마다 일일이 props를 넘겨주지 않고도 컴포넌트 트리 전체에 데이터를 제공할 수 있는 기법입니다.

일반적인 React 어플리케이션에서 데이터는 위에서 아래로 props를 통해 전달되지만, 여러 컴포넌트들에게 전해줘야 하는 prop의 경우, 이 과정이 번거로울 수 있습니다.

context를 이용하면, 트리단계마다 명시적으로 props를 넘겨주지 않아도 많은 컴포넌트가 이 값을 공유하도록 할 수 있습니다.

주의점

context의 주된 용도는 다양한 레벨의 많은 컴포넌트에게 데이터를 전달하는 것입니다. context를 사용하면 컴포넌트를 재사용하기 어려워지므로 꼭 필요할 때 쓰세요.

여러 레벨에 걸쳐 props 넘기는 걸 대체하는 데 context보다 컴포넌트 합성이 더 간단한 해결책일 수도 있습니다.

컴포넌트 합성 예
  • props를 계속 전달하는 번거로운 경우
// Link에서 사용하기 위해 'user'와 'avaterSize'가 계속 props로 하위 컴포넌트에 전달되고 있습니다
<Page user={user} avatarSize={avatarSize} />
// ... 그 아래에 ...
  <PageLayout user={user} avatarSize={avatarSize} />
  // ... 그 아래에 ...
    <NavigationBar user={user} avatarSize={avatarSize} />
    // ... 그 아래에 ...
      <Link href={user.permalink}>
        <Avatar user={user} size={avatarSize} />
      </Link>
  • 컴포넌트 합성하는 경우
function Page(props) {
  const user = props.user;
  const userLink = (
    <Link href={user.permalink}>
      <Avatar user={user} size={props.avatarSize} />
    </Link>
  );
  return <PageLayout userLink={userLink} />;
}

// 이제 이렇게 쓸 수 있습니다.
// 이제 'user'와 'avatarSize'를 하위 컴포넌트에서 알 필요가 없습니다.
<Page user={user} avatarSize={avatarSize} />
  // ... 그 아래에 ...
  <PageLayout userLink={...} />
    // ... 그 아래에 ...
    <NavigationBar userLink={...} />
    // ... 그 아래에 ...
      {props.userLink}

정리

같은 데이터를 트리 안 여러 레벨이 있는 많은 컴포넌트에게 주어야 할 때가 있습니다. 데이터 값이 변할 때마다 모든 하위 컴포넌트에게 알려줄 필요가 있을 때 context를 사용합니다.

(React 공식문서)

Context API

React.createContext

const MyContext = React.createContext(defaultValue);

createContext는 Context 객체를 만듭니다.

Context 객체를 구독하고 있는 컴포넌트를 렌더링할 때 React는 트리 상위에서 가장 가까이 있는 짝이 맞는 Provider로부터 현재 값을 읽습니다.

Context.Provider

<MyContext.Provider value={/* 어떤 값 */}>

Provider는 context를 구독하는 컴포넌트들에게 context의 변화를 알리는 역할을 합니다.

Provider 컴포넌트는 value prop을 받아 하위에 있는 컴포넌트에게 전달합니다.

Provider 하위에 또다른 Provider를 배치하는 것도 가능하며, 이 경우 하위 Provider의 값이 우선시 됩니다.

Provider 하위에서 context를 구독하는 모든 컴포넌트는 Provider의 value prop이 바뀔 때마다 다시 렌더링 됩니다.

export function RepeatingProviderPage() {
  return (
    <FirstProvider>
      <SecondProvider>
        <ExamplePageComponent />
      </SecondProvider>
    </FirstProvider>
  );
}

Context.Consumer

<MyContext.Consumer>
  {value => /* context 값을 이용한 렌더링 */}
</MyContext.Consumer>

context 변화를 구독하는 React 컴포넌트입니다. 이 컴포넌트를 사용하면 함수 컴포넌트 안에서 context를 구독할 수 있습니다.

이때 Context.Consumer의 자식은 함수여야 합니다.

이 함수가 받는 value 매개변수 값은 해당 contextProvider 중, 상위트리에서 가장 가까운 Providervalue prop과 동일합니다.

만약 상위에 Provider가 없다면 value 매개변수 값은 createContext()에 보냈던 defaultValue와 동일합니다.

최근에는 React Hook이 추가되면서 Context.Consumer 대신 useContext를 사용하는 추세입니다.
useContext를 사용하면 Context.Consumer 보다 깔끔하게 Context의 전역데이터에 접근할 수 있습니다.

  • useContext 사용 예시
import React, { useContext } from "react";
import LangContext from "./LangContext";

function Button({ toggleLang }) {
  const lang = useContext(LangContext);
  return <button onClick={toggleLang}>{lang}</button>;
}

useContext를 호출한 컴포넌트는 context 값이 변경되면 항상 리렌더링 될 것입니다.

컴포넌트를 리렌더링 하는 것에 비용이 많이 든다면, 메모이제이션을 사용하여 최적화할 수 있습니다.

Context.displayName

Context 객체는 displayName 문자열 속성을 설정할 수 있습니다.

React 개발자 도구는 이 문자열을 사용해서 context를 어떻게 보여줄 지 결정합니다.

const MyContext = React.createContext(/* some value */);
MyContext.displayName = 'MyDisplayName';

<MyContext.Provider> // "MyDisplayName.Provider" in DevTools

0개의 댓글