Table of contents
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
으로 전달하는 것을 먼저 시도