Prop Drilling 은 props를 오로지 하위 컴포넌트로 전달하는 용도로만 쓰이는 컴포넌트들을 거치면서 React Component 트리의 한 부분에서 다른 부분으로 데이터를 전달하는 과정이다
컴포넌트 간에 데이터를 전달하는 가장 쉽고 빠르게 전달 할 수 있다.
컴포넌트를 잘게 분할해서 prop drilling을 통해 전달하면, 코드를 실행하지 않고 정적으로 따라가는 것만으로도 어떤 데이터가 어디서 사용되는지 쉽게 파악할 수 있으며, 수정도 용이하다. (작은 규모의 어플리케이션에서)
어플리케이션의 규모가 커지고, 컴포넌트의 수가 많아지게 되면서 prop drilling으로 코드가 훨씬 복잡해질 수 있다.
필요보다 많은 props를 전달하다가, 컴포넌트를 분리하는 과정에서 필요하지 않은 props가 계속 남거나 전달되는 문제.
props 전달이 누락되었는데 defaultProps가 과용되었을 때, props가 전달되지 않은 상황을 인지하기가 어려운 문제.
props의 이름이 전달중에 변경되어서 데이터를 추적하기가 쉽지 않게되는 문제.
1) 렌더링 될 컴포넌트를 불필요하게 여러 컴포넌트로 나누지 않는다.
React는 (권장되지는 않지만)단 하나의 컴포넌트에 어플리케이션 전체를 작성하더라도 기술적인 제약은 없다. 따라서 성급하게 불필요한 컴포넌트 쪼개기를 할 필요는 없다. 컴포넌트를 재사용해야할 상황을 기다렸다 분할해도 늦지 않으며, 불필요한 prop drilling도 하지 않을 수 있다.
2) defaultProps를 필수 컴포넌트에 사용하지 않는다.
defaultProps를 사용하면 컴포넌트가 제대로 동작하기 위해 꼭 필요한 props가 전달되지 못한 상황에도 중요한 오류가 가려지게 된다. 따라서 defaultProps를 필수적이지 않은 컴포넌트에만 사용하면 prop drilling으로 인한 문제를 막을 수 있다.
3) 가능한 관련성이 높은 곳에 state를 위치 시킨다.
어떤 데이터가 어플리케이션의 특정 위치에서만 필요하다면 최상위 컴포넌트에 state를 위치시키는 것보다 해당 데이터를 필요로 하는 최소 공통 부모 컴포넌트에서 관리를 하는 것이 가장 효율적이다.
4) 상태관리 도구를 사용한다.
데이터를 필요로 하는 컴포넌트가 정말 깊숙히 위치하고 있다면, React에서 제공하는 Context API를 사용하거나 Redux 등의 외부 상태관리 라이브러리를 사용해서 문제를 해결할 수 있다. 물론 어플리케이션의 모든 곳에서 이와 같은 도구를 사용해야하는 것은 아니지만, 비교적 손쉽게 문제를 해결할 수 있다.
import React from 'react';
function App() {
const data = `드디어 도착`;
return (
<div>
<One data={data} />
</div>
);
}
function One({ data }) {
return (
<div>
<h3>첫번째 컴포넌트 입니다.</h3>
<Two data={data} />
</div>
);
}
function Two({ data }) {
return (
<div>
<h3>두번째 컴포넌트 입니다.</h3>
<Three data={data} />
</div>
);
}
function Three({ data }) {
return (
<div>
<h3>세번째 컴포넌트 입니다.</h3>
<Four data={data} />
</div>
);
}
function Four({ data }) {
return (
<div>
<h3>네번째 컴포넌트 입니다.</h3>
<ShowProps data={data} />
</div>
);
}
function ShowProps({ data }) {
return <h3>{data}</h3>;
}
export default App;
data라는 props를 최하단의 자식 컴포넌트가 데이터를 사용하기 위해 전달하고 있고 이때 상위 부모와 자식 컴포넌트 사이의 깊이가 커져 관리하는것에 대한 어려움이 있다.
위와 같은 문제를 해결하기 위해서는 여러 방법들이 존재하는데 대표적으로는 상태관리 라이브러리를 사용한다 대표적으로는 redux , MobX 등이 존재한다. 또한 useContext라는 React Hook을 사용할 수 있는데 useContext를 사용해서 리팩토링한다.
import React, { useContext } from 'react';
import { DataContext } from './hooks/DataContext';
function App() {
const data = `드디어 도착`;
return (
<DataContext.Provider value={data}>
<One />
</DataContext.Provider>
);
}
function One() {
return (
<div>
<h3>첫번째 컴포넌트 입니다.</h3>
<Two />
</div>
);
}
function Two() {
return (
<div>
<h3>두번째 컴포넌트 입니다.</h3>
<Three />
</div>
);
}
function Three() {
return (
<div>
<h3>세번째 컴포넌트 입니다.</h3>
<Four />
</div>
);
}
function Four() {
return (
<div>
<h3>네번째 컴포넌트 입니다.</h3>
<ShowProps />
</div>
);
}
function ShowProps() {
const data = useContext(DataContext);
return <h3>{data}</h3>;
}
export default App;
다만 useContext를 사용할 경우에는 컴포넌트를 재사용하기 어려워 질 수 있어서 꼭 필요할때만 사용해야 하고 만일 Props drilling을 피하기 위한 목적이라면 Component Composition ( 컴포넌트 합성 )을 먼저 고려해야 한다.