Files
claude-plugins/dev-tools/skills/react-patterns/references/composition-patterns.md
Emanuel Almeida 6b3a6f2698 feat: refactor 30+ skills to Anthropic progressive disclosure pattern
- All SKILL.md files now <500 lines (avg reduction 69%)
- Detailed content extracted to references/ subdirectories
- Frontmatter standardised: only name + description (Anthropic standard)
- New skills: brand-guidelines, spec-coauthor, report-templates, skill-creator
- Design skills: anti-slop guidelines, premium-proposals reference
- Removed non-standard frontmatter fields (triggers, version, author, category)

Plugins affected: infraestrutura, marketing, dev-tools, crm-ops, gestao,
core-tools, negocio, perfex-dev, wordpress, design-media

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 15:05:03 +00:00

4.7 KiB

React - Composition Patterns (Vercel Engineering)

Padroes de composicao para componentes React escalaveis. Fonte: Vercel composition-patterns.

Regra #1: Evitar Boolean Props (CRITICAL)

// NAO: Booleans duplicam estados possiveis
<Composer isThread isEditing={false} showAttachments showFormatting={false} />

// SIM: Variantes explicitas
<ThreadComposer channelId="abc" />
<EditMessageComposer messageId="xyz" />
<ForwardMessageComposer messageId="123" />

Cada boolean duplica os estados possiveis. 5 booleans = 32 combinacoes, maioria impossivel.


Regra #2: Compound Components com Context (HIGH)

// Compound Components: context partilhado, composicao explicita
const ComposerContext = createContext<ComposerContextValue | null>(null)

function ComposerFrame({ children }: { children: React.ReactNode }) {
  return <form>{children}</form>
}

function ComposerInput() {
  const { state, actions: { update }, meta } = use(ComposerContext)
  return (
    <TextInput
      ref={meta.inputRef}
      value={state.input}
      onChangeText={(text) => update(s => ({ ...s, input: text }))}
    />
  )
}

// Exportar como compound
const Composer = {
  Frame: ComposerFrame,
  Input: ComposerInput,
  Submit: ComposerSubmit,
  Footer: ComposerFooter,
}

// Uso: composicao clara
<Composer.Frame>
  <Composer.Input />
  <Composer.Footer>
    <Composer.Submit />
  </Composer.Footer>
</Composer.Frame>

Regra #3: State em Providers (HIGH)

// NAO: Estado preso dentro do componente
function ForwardComposer() {
  const [state, setState] = useState(initialState)
  return <Composer.Frame>...</Composer.Frame>
}

// SIM: State lifted para Provider
function ForwardProvider({ children }: { children: React.ReactNode }) {
  const [state, setState] = useState(initialState)
  const submit = useForwardMessage()
  const inputRef = useRef(null)
  return (
    <Composer.Provider
      state={state}
      actions={{ update: setState, submit }}
      meta={{ inputRef }}
    >
      {children}
    </Composer.Provider>
  )
}

// Qualquer componente dentro do Provider acede ao state
function ForwardDialog() {
  return (
    <ForwardProvider>
      <Dialog>
        <ForwardComposer />
        <MessagePreview />
        <ForwardButton />
      </Dialog>
    </ForwardProvider>
  )
}

function ForwardButton() {
  const { actions } = use(ComposerContext)
  return <Button onPress={actions.submit}>Forward</Button>
}

Principio chave: O boundary do Provider e o que importa, nao o nesting visual.


Regra #4: Interface Generica para Dependency Injection (HIGH)

// Definir interface generica: state + actions + meta
interface ComposerContextValue {
  state: { input: string; attachments: Attachment[]; isSubmitting: boolean }
  actions: { update: (fn: (s: State) => State) => void; submit: () => void }
  meta: { inputRef: React.RefObject<TextInput> }
}

// Multiplos providers implementam a MESMA interface
function ChannelProvider({ channelId, children }) {
  const { state, update, submit } = useGlobalChannel(channelId)
  return <Composer.Provider state={state} actions={{ update, submit }}>{children}</Composer.Provider>
}

function ForwardProvider({ children }) {
  const [state, setState] = useState(initialState)
  return <Composer.Provider state={state} actions={{ update: setState, submit: forward }}>{children}</Composer.Provider>
}

// O mesmo Composer.Input funciona com AMBOS os providers

Principio: UI = pecas reutilizaveis. State = injectado pelo provider. Trocar provider, manter UI.


Regra #5: Children > Render Props (MEDIUM)

// NAO: Render props (inflexivel)
<Composer
  renderHeader={() => <Header />}
  renderFooter={() => <><Formatting /><Emojis /></>}
/>

// SIM: Children (composicao natural)
<Composer.Frame>
  <Header />
  <Composer.Input />
  <Composer.Footer>
    <Formatting />
    <Emojis />
  </Composer.Footer>
</Composer.Frame>

// Excepcao: render props quando parent passa dados
<List data={items} renderItem={({ item }) => <Item item={item} />} />

Regra #6: React 19 APIs (MEDIUM)

// React 19: ref como prop normal (sem forwardRef)
function Input({ ref, ...props }: Props & { ref?: React.Ref<HTMLInputElement> }) {
  return <input ref={ref} {...props} />
}

// React 19: use() em vez de useContext()
const value = use(MyContext) // pode ser chamado condicionalmente

Checklist Composicao

  • Zero boolean props para variantes (usar componentes explicitos)
  • Compound components com context partilhado
  • State em Provider (nao dentro do componente UI)
  • Interface generica (state/actions/meta)
  • Children para composicao, render props so para data passthrough
  • React 19: use() e ref como prop