Homepage (site/index.html): integration-v14 promoted, Writings section integrated with 33 pieces clustered by type (stories/essays/miscellany), Writings welcome lightbox, content frame at 98% opacity. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
88 lines
1.8 KiB
Markdown
88 lines
1.8 KiB
Markdown
---
|
|
title: Prefer Composing Children Over Render Props
|
|
impact: MEDIUM
|
|
impactDescription: cleaner composition, better readability
|
|
tags: composition, children, render-props
|
|
---
|
|
|
|
## Prefer Children Over Render Props
|
|
|
|
Use `children` for composition instead of `renderX` props. Children are more
|
|
readable, compose naturally, and don't require understanding callback
|
|
signatures.
|
|
|
|
**Incorrect (render props):**
|
|
|
|
```tsx
|
|
function Composer({
|
|
renderHeader,
|
|
renderFooter,
|
|
renderActions,
|
|
}: {
|
|
renderHeader?: () => React.ReactNode
|
|
renderFooter?: () => React.ReactNode
|
|
renderActions?: () => React.ReactNode
|
|
}) {
|
|
return (
|
|
<form>
|
|
{renderHeader?.()}
|
|
<Input />
|
|
{renderFooter ? renderFooter() : <DefaultFooter />}
|
|
{renderActions?.()}
|
|
</form>
|
|
)
|
|
}
|
|
|
|
// Usage is awkward and inflexible
|
|
return (
|
|
<Composer
|
|
renderHeader={() => <CustomHeader />}
|
|
renderFooter={() => (
|
|
<>
|
|
<Formatting />
|
|
<Emojis />
|
|
</>
|
|
)}
|
|
renderActions={() => <SubmitButton />}
|
|
/>
|
|
)
|
|
```
|
|
|
|
**Correct (compound components with children):**
|
|
|
|
```tsx
|
|
function ComposerFrame({ children }: { children: React.ReactNode }) {
|
|
return <form>{children}</form>
|
|
}
|
|
|
|
function ComposerFooter({ children }: { children: React.ReactNode }) {
|
|
return <footer className='flex'>{children}</footer>
|
|
}
|
|
|
|
// Usage is flexible
|
|
return (
|
|
<Composer.Frame>
|
|
<CustomHeader />
|
|
<Composer.Input />
|
|
<Composer.Footer>
|
|
<Composer.Formatting />
|
|
<Composer.Emojis />
|
|
<SubmitButton />
|
|
</Composer.Footer>
|
|
</Composer.Frame>
|
|
)
|
|
```
|
|
|
|
**When render props are appropriate:**
|
|
|
|
```tsx
|
|
// Render props work well when you need to pass data back
|
|
<List
|
|
data={items}
|
|
renderItem={({ item, index }) => <Item item={item} index={index} />}
|
|
/>
|
|
```
|
|
|
|
Use render props when the parent needs to provide data or state to the child.
|
|
Use children when composing static structure.
|