본문 바로가기

이것저것 스터디📚/React - 공식 문서

React 공식문서 -Passing Data Deeply with Context(context로 데이터 깊숙이 전달하기)

Context를 사용하면 부모 컴포넌트가 props를  통해 명시적으로 전달하지 않고도 깊이 여부와 무관하게 그 아래 트리의 모든 컴포넌트에서 일부 정보를 사용할 수 있다.

The problem with passing props(props 전달의 문제)

- props 전달은 UI 트리를 통해 데이터를 사용하는 컴포넌트로 명시적으로 연결할 수 있는 좋은 방법이다.

- 하지만, 가장 가까운 공통 조상이 데이터가 필요한 컴포넌트에서 멀리 떨어져 있을 수 있으며, state를 높이 끓어올리면 "prop drilling"이 발생할 수 있다.


Context: an alternative to passing props(Context: props 전달의 대안)

- Context를 사용하면 상위 컴포넌트가 그 아래 전체 트리에 데이터를 제공할 수 있다.

- Context를 사용하는 방법

   1. context를 생성한다.

   2. 데이터가 필요한 컴포넌트에서 해당 context를 사용한다.

   3. 데이터를 지정하는 컴포넌트에서 해당 context를 제공한다.


Step 1 : 컨텍스트 생성(Step 1: Context 만들기)

 

- 가장 먼저 context를 만들고 다른 컴포넌트에서 사용할 수 있도록 파일에서 내보내기를 한다.

// LevelContext.js 
import { createContext } from  'react' ;

export  const LevelContext = createContext( 1 );

- createContext의 유일한 인수는 기본값이다.


Step 2 : Use the context(Step 2: context 사용하기)

- React와 context에서 useContext Hook을 가져온다.

import { useContext } from  'react' ;
import { LevelContext } from  './LevelContext.js' ;

- context를 사용할 곳에서 context 값을 읽는다.

// Heading.js 
export default function Heading({ children }) {
  const level = useContext(LevelContext);
  // ...
}

- useContext는 Hook이기 때문에, useState 및 useReducer와 마찬가지로 React 컴포넌트의 최상단에서만 Hook을 호출할 수 있다.

- 하지만 아직, context를 제공하지 않았기 때문에, React는 어디서 그것을 가져와야 할지 모르기 때문에, React는 이전 단계에서 context에서 지정한 기본 값을 사용한다.


Step 3 : Provide the context(Step 3: context 제공하기)

- context provider로 감싸서 context를 제공할 수 있다.

// App.js 
import Heading from './Heading.js';
import Section from './Section.js';

export default function Page() {
  return (
    <Section level={1}>
      <Heading>Title</Heading>
      <Section level={2}>
        <Heading>Heading</Heading>
        <Heading>Heading</Heading>
        <Heading>Heading</Heading>
        <Section level={3}>
          <Heading>Sub-heading</Heading>
          <Heading>Sub-heading</Heading>
          <Heading>Sub-heading</Heading>
          <Section level={4}>
            <Heading>Sub-sub-heading</Heading>
            <Heading>Sub-sub-heading</Heading>
            <Heading>Sub-sub-heading</Heading>
          </Section>
        </Section>
      </Section>
    </Section>
  );
}
// Section.js 
import { LevelContext } from './LevelContext.js';

export default function Section({ level, children }) {
  return (
    <section className="section">
      <LevelContext.Provider value={level}>
        {children}
      </LevelContext.Provider>
    </section>
  );
}

- context provider는 위 예제에서 <Section> 안에 있는 컴포넌트가 prop으로 전달받은 level을 LevelContext를 요청하면 이 level을 제공하라고 지시한다.

// Heading.js 
import { useContext } from 'react';
import { LevelContext } from './LevelContext.js';

export default function Heading({ children }) {
  const level = useContext(LevelContext);
  switch (level) {
    case 1:
      return <h1>{children}</h1>;
    case 2:
      return <h2>{children}</h2>;
    case 3:
      return <h3>{children}</h3>;
    case 4:
      return <h4>{children}</h4>;
    case 5:
      return <h5>{children}</h5>;
    case 6:
      return <h6>{children}</h6>;
    default:
      throw Error('Unknown level: ' + level);
  }
}

- Heading 컴포넌트는 useContext(LevelContext)를 사용하여 위의 LevelContext 값에 가장 가까운 값을 요청한다.


Using and providing context from the same component(동일한 컴포넌트에서 context 사용 및 제공)

위의 예제 코드를 보면 여전히 section의 level 값을 prop를 통해 수동으로 지정하고 있다.

 

아래와 같은 방법을 사용하면 section 컴포넌트 내에서 LevelContext의 level을 읽고 level + 1을 자동으로 전달할 수 있다.

// Section.js 
import { useContext } from 'react';
import { LevelContext } from './LevelContext.js';

export default function Section({ children }) {
  const level = useContext(LevelContext);
  return (
    <section className="section">
      <LevelContext.Provider value={level + 1}>
        {children}
      </LevelContext.Provider>
    </section>
  );
}

Context passes through intermediate components(Context는 중간 컴포넌트들을 통과합니다)

 

- context를 제공하는 컴포넌트와 context를 사용하는 컴포넌트 사이에 원하는 만큼의 컴포넌트를 삽입할 수 있다.

- context를 사용하면 "주변 환경에 적응"하고 렌더링되는 위치(context)에 따라 다르게 표시되는 컴포넌트를 작성할 수 있다.

- React에서 위에서 오는 context를 재정의하는 유일한 방법은 children을 다른 값으로 context provider로 감싸는 것이다.

- createContext()로 만드는 각 context는 다른 context와 완전히 분리되어 있고, 특정 context를 사용하거나 제공하는 컴포넌트를 함께 묶는다.

- 하나의 컴포넌트가 문제없이 다양한 context를 사용하거나 제공할 수 있다.


Before you use context(context를 사용하기 전에)

- props를 몇 단계 깊이 전달해야 한다고 해서 해당 정보를 context에 넣어야 한다는 의미는 아니다.

- context를 사용하기 전에 고려해야 할 몇 가지 대안

   1. props 전달로 시작하자.

      수십 개의 props를 수십 개의 컴포넌트에 전달해야 하는 경우가 드물지 않고, props를 사용하여 데이터 흐름을 명확하게 만드는 것에        만족할 수도 있다.

   2. 컴포넌트를 추출하고 JSX를 children으로 전달하자.

      만약 일부 데이터를 해당 데이터를 사용하지 않는 중간 컴포넌트의 여러 레이어를 거쳐 전달한다면, 이는 종종 일부 컴포넌트를

      추출하는 것을 잊었다는 것을 의미한다. 따라서, 이런 경우 children을 prop으로 사용하게 만들어야 한다. 이는 데이터를 지정하는

      컴포넌트와 데이터를 필요로 하는 컴포넌트 사이의 레이어 수가 줄어들게 할 수 있다.

- 만약 위 두가지의 접근 방식이 모두 적합하지 않은 경우 context를 고려해야한다.


Use cases for context(context 사용 사례)

- 테마(Theme) : 앱에서 사용자가 앱의 모양을 변경할 수 있는 경우에는 앱 상단에 context provider를 배치하고 시각적 모양을 조정해야 하는 컴포넌트에서 해당 context를 사용할 수 있다.

- 현재 계정(Current account) : 많은 컴포넌트에서 현재 로그인한 사용자를 알아야 하는 경우, context를 사용하면 트리의 어느 곳에서나 편리하게 읽을 수 있다.

- 라우팅(Routing) : 대부분의 라우팅 솔루션은 내부적으로 context를 사용하여 현재 경로를 유지한다. 자체 라우터를 구축하는 경우에도 context를 사용할 수 있다.

- state 관리 : 앱이 복잡해질 수록 많은 state가 앱 상단에 가까워질 수 있다. 이때 앱 상단과 멀리 떨어진 컴포넌트에서 state를 변경하고자 할때, context와 함께 reducer를 사용하여 복잡한 state를 관리하고 번거로움 없이 멀리 떨어진 컴포넌트에 전달할 수 있다.

 

- context는 정적 값에만 국한되는 것이 아닌, 다음 렌더링에서 다른 값을 전달하면 React는 아래에서 이를 읽는 모든 컴포넌트를 업데이트 한다.


* 참고 : React 공식문서(https://react-ko.dev/learn)