963 markdown files reformatted with the repository's pinned prettier so pnpm format:check covers the folded tree like every other repo file. The formatter's embedded-language pass also normalized code fences (TS semicolons, closed HTML tags in examples, lowercased CSS hex colors, one renumbered list that skipped an index). Alphanumeric token deltas vs the fold commit were audited file-by-file; all are formatter-equivalent markup normalizations plus the four sanitized skills.
2.5 KiB
2.5 KiB
title, impact, impactDescription, tags
| title | impact | impactDescription | tags |
|---|---|---|---|
| Use Compound Components | HIGH | enables flexible composition without prop drilling | composition, compound-components, architecture |
Use Compound Components
Structure complex components as compound components with a shared context. Each subcomponent accesses shared state via context, not props. Consumers compose the pieces they need.
Incorrect (monolithic component with render props):
function Composer({
renderHeader,
renderFooter,
renderActions,
showAttachments,
showFormatting,
showEmojis,
}: Props) {
return (
<form>
{renderHeader?.()}
<Input />
{showAttachments && <Attachments />}
{renderFooter ? (
renderFooter()
) : (
<Footer>
{showFormatting && <Formatting />}
{showEmojis && <Emojis />}
{renderActions?.()}
</Footer>
)}
</form>
);
}
Correct (compound components with shared context):
const ComposerContext = createContext<ComposerContextValue | null>(null);
function ComposerProvider({ children, state, actions, meta }: ProviderProps) {
return <ComposerContext value={{ state, actions, meta }}>{children}</ComposerContext>;
}
function ComposerFrame({ children }: { children: React.ReactNode }) {
return <form>{children}</form>;
}
function ComposerInput() {
const {
state,
actions: { update },
meta: { inputRef },
} = use(ComposerContext);
return (
<TextInput
ref={inputRef}
value={state.input}
onChangeText={(text) => update((s) => ({ ...s, input: text }))}
/>
);
}
function ComposerSubmit() {
const {
actions: { submit },
} = use(ComposerContext);
return <Button onPress={submit}>Send</Button>;
}
// Export as compound component
const Composer = {
Provider: ComposerProvider,
Frame: ComposerFrame,
Input: ComposerInput,
Submit: ComposerSubmit,
Header: ComposerHeader,
Footer: ComposerFooter,
Attachments: ComposerAttachments,
Formatting: ComposerFormatting,
Emojis: ComposerEmojis,
};
Usage:
<Composer.Provider state={state} actions={actions} meta={meta}>
<Composer.Frame>
<Composer.Header />
<Composer.Input />
<Composer.Footer>
<Composer.Formatting />
<Composer.Submit />
</Composer.Footer>
</Composer.Frame>
</Composer.Provider>
Consumers explicitly compose exactly what they need. No hidden conditionals. And the state, actions and meta are dependency-injected by a parent provider, allowing multiple usages of the same component structure.