React 공부할끄니까! Prop Drilling을 해결하는 Context API?!

React 공부할끄니까! Prop Drilling을 해결하는 Context API?!

·

3 min read

Prop Drilling.

보통의 경우에 prop를 부모 컴포넌트에서 자식 컴포넌트로 내려준다. 그러나 중간에 많은 컴포넌트들을 거쳐야 한다. → prop이 불필요한데 거쳐야 하거나, 많은 컴포넌트에서 동일한 prop이 필요할 때 모두 건내준다면 불편할 수 있음

이런 상황을 Prop drilling이라고 하는데, 이 문제를 해결하기 위해 Context API가 나왔다.

Context.

Context는 부모 컴포넌트 아래 트리 전체에 데이터를 전달할 수 있도록 해준다.

<Section>  
    <Heading level={3}>About</Heading>  
    <Heading level={3}>Photos</Heading>  
    <Heading level={3}>Videos</Heading>  
</Section>

위 코드와 같이 Heading 컴포넌트별로 level 프롭을 모두 전달해야 하는 상황은 번거롭고, 공통요소에 대한 변경에 용이하지 않다.

<Section level={3}>  
    <Heading>About</Heading>  
    <Heading>Photos</Heading>  
    <Heading>Videos</Heading>  
</Section>

코드를 위와 같이 변경할 수 있다면 최선일 것이다. → 이 경우에 가장 가까운 컴포넌트인 <Section />의 레벨을 알 필요가 있고, 자식에게 본인 트리에서 상위 컴포넌트에 있는 데이터를 "요청하는" 방법이 필요함

위와 같이 변경하려면 Props가 아닌 Context가 필요한 시점이다. → Context는 가까운 자식 리액트 노드에서 사용되거나, 멀리 떨어진 자식 리액트 노드에서 사용 가능함

import { createContext } from 'react'
export const LevelContext = createContext(0)
  • createContext를 사용해 LevelContext를 생성하고,
import { useContext } from 'react'
import { LevelContext } from './LevelContext'

// Prop인 level이 삭제됨
export default function Heading({ children }) {
    const level = useContext(LevelContext)
    // ... 
}
  • 데이터가 필요한 컴포넌트에서 Context를 사용하고,
    • useContext 훅을 사용한다. 때문에 컴포넌트 내에서만 호출함. (반복문, 조건문에선 불가)
import { LevelContext } from './LevelContext'

export default function Section({ level, children }) { 
    return ( 
        <section className="section">
            <LevelContext.Provider value={level}> 
                { children } 
            </LevelContext.Provider> 
        </section>
    )
}
  • 데이터를 지정하는 컴포넌트에서 Context를 제공한다.
    • 그러기 위해서 부모 컴포넌트가 자식 컴포넌트들을 Context Provider로 감싸줘야 함
    • 이제 children은 UI 트리에서 가장 가까운 <LevelContext.Provider>의 값을 사용함
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>
  );
}

위 예시를 보면 Title은 level=1, Heading은 level=2, Sub-heading은 level=3, Sub-sub-heading은 level=4를 사용한다. → UI 트리에서 가장 가까운 Context Provider의 값을 가져와 사용하기 때문임

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

위와 같이 <Section> 컴포넌트에서 LevelContext를 사용하도록 하고, Context의 값을 level + 1로 지정하면,

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

export default function Page() {
  return (
    <Section>
      <Heading>Title</Heading>
      <Section>
        <Heading>Heading</Heading>
        <Heading>Heading</Heading>
        <Heading>Heading</Heading>
        <Section>
          <Heading>Sub-heading</Heading>
          <Heading>Sub-heading</Heading>
          <Heading>Sub-heading</Heading>
          <Section>
            <Heading>Sub-sub-heading</Heading>
            <Heading>Sub-sub-heading</Heading>
            <Heading>Sub-sub-heading</Heading>
          </Section>
        </Section>
      </Section>
    </Section>
  );
}

위 예제와 같이 모든 <Section> 컴포넌트에 level을 지정할 필요가 없어진다.

Result.

  • Context는 컴포넌트가 트리 상 아래에 위치한 모든 곳에 데이터를 제공함
  • Context를 전달하는 방법은
    • createContext(defaultValue)로 생성하고 내보냄
    • useContext(CreatedContext) 훅으로 자식 컴포넌트가 읽을 수 있음
    • 자식 컴포넌트를 <CreatedContext.Provider value={...}>로 감싸 부모로부터 Context를 받도록함
  • Context는 중간의 어떤 컴포넌트도 지나칠 수 있음
  • Context를 활용해 "주변에 적응하는" 컴포넌트를 작성할 수 있음
  • Context 사용 전에 props를 전달하거나 JSX를 children으로 전달하는 것을 먼저 시도

References.