logo
Back to blogs

Understanding Composition To Avoid Prop Drilling

Mar 10, 2023 - 5 min read

Understanding Composition To Avoid Prop Drilling

The Composition and Layout Components Pattern

The composition and Layout Components Pattern helps to avoid prop drilling and makes the code more readable and maintainable. The composition pattern is a design pattern that uses the React children prop to compose components together. Let's skip to the end here. It's surprising what you can accomplish by passing react elements rather than treating components as uncrossable boundaries. We'll have a practical example in our exercise, so let me show you a quick and easy contrived example to explain what we'll be doing here:

AvoidPropDrilling.tsx
1function App() { 2 const [count, setCount] = useState(0); 3 const increment = () => setCount((c) => c + 1); 4 return <Child count={count} increment={increment} />; 5} 6function Child({ count, increment }: { count: number; increment: () => void }) { 7 return ( 8 <div> 9 <strong> 10 I am a child and I don't actually use count or increment. My child does 11 though so I have to accept those as props and forward them along. 12 </strong> 13 <GrandChild count={count} onIncrementClick={increment} /> 14 </div> 15 ); 16} 17function GrandChild({ 18 count, 19 onIncrementClick, 20}: { 21 count: number; 22 onIncrementClick: () => void; 23}) { 24 return ( 25 <div> 26 <small>I am a grand child and I just pass things off to a button</small> 27 <button onClick={onIncrementClick}>{count}</button> 28 </div> 29 ); 30}

This prop drilling stuff is one of the reasons so many people have jumped onto state management solutions, whether it be libraries or React context. However, if we restructure things a bit, we'll notice that things get quite a bit easier without losing the flexibility we're hoping for.

AvoidPropDrilling.tsx
1function App() { 2 const [count, setCount] = useState(0); 3 const increment = () => setCount((c) => c + 1); 4 return ( 5 <Child 6 grandChild={ 7 <GrandChild 8 button={<button onClick={onIncrementClick}>{count}</button>} 9 /> 10 } 11 /> 12 ); 13} 14function Child({ grandChild }: { grandChild: React.ReactNode }) { 15 return ( 16 <div> 17 <strong> 18 I am a child and I don't actually use count or increment. My child does 19 though so I have to accept those as props and forward them along. 20 </strong> 21 {grandChild} 22 </div> 23 ); 24} 25function GrandChild({ button }: { button: React.ReactNode }) { 26 return ( 27 <div> 28 <small>I am a grand child and I just pass things off to a button</small> 29 {button} 30 </div> 31 ); 32}