Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/tomevault-io/skills-registry --skill composition-patterns명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
| Use when this capability is needed.
> Use when this capability is needed.
Review architecture and API design for the vfs-s3 project. Use when the user mentions @architect, asks to review an issue's design, discuss module boundaries, API shape, or architectural decisions for vfs-s3. Also trigger when the user wants to create an ADR (Architecture Decision Record) or evaluate a technical approach for the project. Intended for dispatch from Codex automation or Claude routines; GitHub trigger phrase: @vfs-s3-bot please prepare design doc Use when this capability is needed.
SOC 직업 분류 기준
SKILL.md 표시 중
| name | composition-patterns |
| description | | Use when this capability is needed. |
Composition patterns for flexible and maintainable React components. Avoid boolean prop abuse, use compound components, state lifting, and composing internals.
| Priority | Category | Impact | Description |
|---|---|---|---|
| 1 | Component Architecture | HIGH | Component structuring |
| 2 | State Management | MEDIUM | State management patterns |
| 3 | Implementation Patterns | MEDIUM | Implementation patterns |
| 4 | React 19 APIs | MEDIUM | React 19 changes |
Impact: CRITICAL
Boolean props lead to combinatorial explosion. Use composition instead.
// ❌ Boolean prop explosion - unmaintainable
function Composer({
isThread,
isDMThread,
isEditing,
isForwarding,
}: Props) {
return (
<form>
{isDMThread ? <DMField /> : isThread ? <ThreadField /> : null}
{isEditing ? <EditActions /> : isForwarding ? <ForwardActions /> : <DefaultActions />}
</form>
)
}
// ✅ Composition - explicit variants
function ThreadComposer({ channelId }: { channelId: string }) {
return (
<Composer.Frame>
<Composer.Input />
<AlsoSendToChannelField id={channelId} />
<Composer.Footer>
<Composer.Submit />
</Composer.Footer>
</Composer.Frame>
)
}
function EditComposer() {
return (
<Composer.Frame>
<Composer.Input />
<Composer.Footer>
<Composer.CancelEdit />
<Composer.SaveEdit />
</Composer.Footer>
</Composer.Frame>
)
}
Impact: HIGH
Structure complex components into subcomponents connected by shared context.
// ❌ Monolithic + render props
function Composer({
renderHeader,
renderFooter,
showAttachments,
}: Props) {
return (
<form>
{renderHeader?.()}
<Input />
{showAttachments && <Attachments />}
{renderFooter?.()}
</form>
)
}
// ✅ Compound components + shared context
const ComposerContext = createContext<ComposerContextValue | null>(null)
function ComposerProvider({ children, state, actions, meta }: ProviderProps) {
return (
<ComposerContext value={{ state, actions, meta }}>
{children}
</ComposerContext>
)
}
function ComposerInput() {
const { state, actions: { update } } = use(ComposerContext)
return <TextInput value={state.input} onChangeText={text => update(s => ({ ...s, input: text }))} />
}
// Export as compound component
= {
: ,
: ,
: ,
: ,
: ,
}
<. state={state} actions={actions} meta={meta}>
</.>
Impact: MEDIUM
Provider knows state implementation, UI only uses context interface.
// ❌ UI coupled to state implementation
function ChannelComposer({ channelId }: { channelId: string }) {
const state = useGlobalChannelState(channelId) // Coupled to specific implementation
const { submit } = useChannelSync(channelId)
return <Composer.Input value={state.input} />
}
// ✅ State management separated in Provider
function ChannelProvider({ channelId, children }: Props) {
const { state, update, submit } = useGlobalChannel(channelId)
return (
<Composer.Provider state={state} actions={{ update, submit }}>
{children}
</Composer.Provider>
)
}
// UI only needs interface
function ChannelComposer() {
return (
<Composer.Frame>
<Composer.Input /> {/* Reads state from context */}
<Composer.Submit />
</Composer.Frame>
)
}
Impact: HIGH
Define generic interface with 3 parts: state, actions, meta.
// Generic interface - any provider can implement
interface ComposerContextValue {
state: {
input: string
attachments: Attachment[]
isSubmitting: boolean
}
actions: {
update: (updater: (state: State) => State) => void
submit: () => void
}
meta: {
inputRef: React.RefObject<TextInput>
}
}
// Provider A: Local state
function ForwardMessageProvider({ children }) {
const [state, setState] = useState(initialState)
return <ComposerContext value={{ state, actions: { update: setState, submit }, meta }}>{children}</ComposerContext>
}
// Provider B: Global synced state
function ChannelProvider({ channelId, children }) {
const { state, update, submit } = useGlobalChannel(channelId)
return
}
Impact: HIGH
Lift state to Provider so sibling components can access it.
// ❌ State trapped inside component
function ForwardMessageDialog() {
return (
<Dialog>
<ForwardMessageComposer /> {/* state trapped here */}
<MessagePreview /> {/* Cannot access state! */}
<ForwardButton /> {/* Cannot call submit! */}
</Dialog>
)
}
// ✅ Lift state to Provider
function ForwardMessageDialog() {
return (
<ForwardMessageProvider>
<Dialog>
<ForwardMessageComposer />
<MessagePreview /> {/* Access state via context */}
<ForwardButton /> {/* Call submit via context */}
</Dialog>
</ForwardMessageProvider>
)
}
// Anywhere inside Provider can access state/actions
function ForwardButton() {
const { actions } = use(ComposerContext)
return <Button onPress={actions.submit}>Forward</Button>
}
Impact: MEDIUM
Create explicit variant components instead of boolean props.
// ❌ Unclear what UI renders
<Composer isThread isEditing={false} channelId="abc" showAttachments />
// ✅ Immediately clear
<ThreadComposer channelId="abc" />
<EditMessageComposer messageId="xyz" />
<ForwardMessageComposer messageId="123" />
Impact: MEDIUM
Use children for composition instead of renderX props.
// ❌ Render props - hard to read
<Composer
renderHeader={() => <CustomHeader />}
renderFooter={() => <><Formatting /><Emojis /></>}
/>
// ✅ Children - natural composition
<Composer.Frame>
<CustomHeader />
<Composer.Input />
<Composer.Footer>
<Composer.Formatting />
<Composer.Emojis />
</Composer.Footer>
</Composer.Frame>
When render props appropriate: Parent needs to pass data to children
<List data={items} renderItem={({ item, index }) => <Item item={item} />} />
⚠️ React 19+ only. Skip this section for React 18 or below.
// ❌ forwardRef (unnecessary in React 19)
const ComposerInput = forwardRef<TextInput, Props>((props, ref) => {
return <TextInput ref={ref} {...props} />
})
// ✅ ref as regular prop
function ComposerInput({ ref, ...props }: Props & { ref?: React.Ref<TextInput> }) {
return <TextInput ref={ref} {...props} />
}
// ❌ useContext (React 19)
const value = useContext(MyContext)
// ✅ use() - conditional calls possible
const value = use(MyContext)
| Check | Rule |
|---|---|
| [ ] | 3+ Boolean props? → Refactor to composition |
| [ ] | Complex conditional rendering? → Create explicit variants |
| [ ] | State trapped in component? → Lift to Provider |
| [ ] | renderX props? → Change to children |
| [ ] | React 19? → Remove forwardRef, use use() |
/react-best-practices - Performance optimization (waterfall, bundle, rendering)/web-design-guidelines - UI/UX quality (accessibility, interaction)/design-patterns - General design patternsConverted and distributed by TomeVault — claim your Tome and manage your conversions.