/install react-composition
React Composition Patterns
Composition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier to work with as they scale.
When to Apply
- Refactoring components with many boolean props
- Building reusable component libraries
- Designing flexible component APIs
- Working with compound components or context providers
Pattern Overview
| # | Pattern | Impact |
|---|---|---|
| 1 | Avoid Boolean Props | CRITICAL |
| 2 | Compound Components | HIGH |
| 3 | Context Interface (DI) | HIGH |
| 4 | State Lifting | HIGH |
| 5 | Explicit Variants | MEDIUM |
| 6 | Children Over Render Props | MEDIUM |
Installation
OpenClaw / Moltbot / Clawbot
npx clawhub@latest install react-composition
1. Avoid Boolean Prop Proliferation
Don't add boolean props like isThread, isEditing, isDMThread to customize
behavior. Each boolean doubles possible states and creates unmaintainable
conditional logic. Use composition instead.
// BAD — boolean props create exponential complexity
function Composer({ isThread, isDMThread, isEditing, isForwarding }: Props) {
return (
\x3Cform>
\x3CInput />
{isDMThread ? \x3CAlsoSendToDMField /> : isThread ? \x3CAlsoSendToChannelField /> : null}
{isEditing ? \x3CEditActions /> : isForwarding ? \x3CForwardActions /> : \x3CDefaultActions />}
\x3C/form>
)
}
// GOOD — composition eliminates conditionals
function ChannelComposer() {
return (
\x3CComposer.Frame>
\x3CComposer.Input />
\x3CComposer.Footer>\x3CComposer.Attachments />\x3CComposer.Submit />\x3C/Composer.Footer>
\x3C/Composer.Frame>
)
}
function ThreadComposer({ channelId }: { channelId: string }) {
return (
\x3CComposer.Frame>
\x3CComposer.Input />
\x3CAlsoSendToChannelField id={channelId} />
\x3CComposer.Footer>\x3CComposer.Submit />\x3C/Composer.Footer>
\x3C/Composer.Frame>
)
}
Each variant is explicit about what it renders. Shared internals without a monolithic parent.
2. Compound Components
Structure complex components with shared context. Each subcomponent accesses state via context, not props. Export as a namespace object.
const ComposerContext = createContext\x3CComposerContextValue | null>(null)
function ComposerProvider({ children, state, actions, meta }: ProviderProps) {
return \x3CComposerContext value={{ state, actions, meta }}>{children}\x3C/ComposerContext>
}
function ComposerInput() {
const { state, actions: { update }, meta: { inputRef } } = use(ComposerContext)
return \x3CTextInput ref={inputRef} value={state.input}
onChangeText={(t) => update((s) => ({ ...s, input: t }))} />
}
const Composer = {
Provider: ComposerProvider, Frame: ComposerFrame,
Input: ComposerInput, Submit: ComposerSubmit, Footer: ComposerFooter,
}
// Consumers compose exactly what they need
\x3CComposer.Provider state={state} actions={actions} meta={meta}>
\x3CComposer.Frame>
\x3CComposer.Input />
\x3CComposer.Footer>\x3CComposer.Formatting />\x3CComposer.Submit />\x3C/Composer.Footer>
\x3C/Composer.Frame>
\x3C/Composer.Provider>
3. Generic Context Interface (Dependency Injection)
Define a generic interface with state, actions, and meta. Any provider
implements this contract — enabling the same UI to work with different state
implementations. The provider is the only place that knows how state is managed.
interface ComposerContextValue {
state: { input: string; attachments: Attachment[]; isSubmitting: boolean }
actions: { update: (fn: (s: ComposerState) => ComposerState) => void; submit: () => void }
meta: { inputRef: React.RefObject\x3CTextInput> }
}
// Provider A: Local state for ephemeral forms
function ForwardMessageProvider({ children }: { children: React.ReactNode }) {
const [state, setState] = useState(initialState)
return (
\x3CComposerContext value={{ state, actions: { update: setState, submit: useForwardMessage() },
meta: { inputRef: useRef(null) } }}>{children}\x3C/ComposerContext>
)
}
// Provider B: Global synced state for channels
function ChannelProvider({ channelId, children }: Props) {
const { state, update, submit } = useGlobalChannel(channelId)
return (
\x3CComposerContext value={{ state, actions: { update, submit },
meta: { inputRef: useRef(null) } }}>{children}\x3C/ComposerContext>
)
}
Swap the provider, keep the UI. Same Composer.Input works with both.
4. Lift State into Providers
Move state into dedicated provider components so sibling components outside the main UI can access and modify state without prop drilling or refs.
// BAD — state trapped inside component; siblings can't access it
function ForwardMessageComposer() {
const [state, setState] = useState(initialState)
return \x3CComposer.Frame>\x3CComposer.Input />\x3CComposer.Footer />\x3C/Composer.Frame>
}
function ForwardMessageDialog() {
return (
\x3CDialog>
\x3CForwardMessageComposer />
\x3CMessagePreview /> {/* Can't access composer state */}
\x3CForwardButton /> {/* Can't call submit */}
\x3C/Dialog>
)
}
// GOOD — state lifted to provider; any descendant can access it
function ForwardMessageProvider({ children }: { children: React.ReactNode }) {
const [state, setState] = useState(initialState)
const submit = useForwardMessage()
return (
\x3CComposer.Provider state={state} actions={{ update: setState, submit }}
meta={{ inputRef: useRef(null) }}>{children}\x3C/Composer.Provider>
)
}
function ForwardMessageDialog() {
return (
\x3CForwardMessageProvider>
\x3CDialog>
\x3CForwardMessageComposer />
\x3CMessagePreview /> {/* Reads state from context */}
\x3CForwardButton /> {/* Calls submit from context */}
\x3C/Dialog>
\x3C/ForwardMessageProvider>
)
}
function ForwardButton() {
const { actions } = use(Composer.Context)
return \x3CButton onPress={actions.submit}>Forward\x3C/Button>
}
Key insight: Components that need shared state don't have to be visually nested — they just need to be within the same provider.
5. Explicit Variant Components
Instead of one component with many boolean props, create explicit variants. Each composes the pieces it needs — self-documenting, no impossible states.
// BAD — what does this render?
\x3CComposer isThread isEditing={false} channelId="abc" showAttachments showFormatting={false} />
// GOOD — immediately clear
\x3CThreadComposer channelId="abc" />
\x3CEditMessageComposer messageId="xyz" />
\x3CForwardMessageComposer messageId="123" />
Each variant is explicit about its provider/state, UI elements, and actions.
6. Children Over Render Props
Use children for composition instead of renderX props. Children are more
readable and compose naturally.
// BAD — render props
\x3CComposer
renderHeader={() => \x3CCustomHeader />}
renderFooter={() => \x3C>\x3CFormatting />\x3CEmojis />\x3C/>}
/>
// GOOD — children composition
\x3CComposer.Frame>
\x3CCustomHeader />
\x3CComposer.Input />
\x3CComposer.Footer>\x3CComposer.Formatting />\x3CSubmitButton />\x3C/Composer.Footer>
\x3C/Composer.Frame>
When render props are appropriate: When the parent needs to pass data back
(e.g., renderItem={({ item, index }) => ...}).
Decision Guide
- Component has 3+ boolean props? → Extract explicit variants (1, 5)
- Component has render props? → Convert to compound components (2, 6)
- Siblings need shared state? → Lift state to provider (4)
- Same UI, different data sources? → Generic context interface (3)
- Building a component library? → Apply all patterns together
- 确保已安装 OpenClaw(本地或 Docker 部署)
- 在对话框中输入安装命令:
/install react-composition - 安装完成后,直接呼叫该 Skill 的名称或使用
/react-composition触发 - 根据 Skill 的参数说明提供必要输入,即可获得结构化输出
React Composition 是什么?
React composition patterns for scalable component architecture. Use when refactoring components with boolean prop proliferation, building flexible component libraries, designing reusable component APIs, or working with compound components and context providers. 它是一个面向 Claude Code / OpenClaw 的 AI Agent Skill 插件,目前累计下载 843 次。
如何安装 React Composition?
在 OpenClaw 或 Claude Code 对话框中运行命令「/install react-composition」即可一键安装,无需额外配置。
React Composition 是免费的吗?
是的,React Composition 完全免费(开源免费),可自由下载、安装和使用。
React Composition 支持哪些平台?
React Composition 跨平台运行,可在任意部署了 OpenClaw / Claude Code 的环境中使用(cross-platform)。
谁开发了 React Composition?
由 wpank(@wpank)开发并维护,当前版本 v1.0.0。