# React - Composition Patterns (Vercel Engineering) Padroes de composicao para componentes React escalaveis. Fonte: Vercel composition-patterns. ## Regra #1: Evitar Boolean Props (CRITICAL) ```tsx // NAO: Booleans duplicam estados possiveis // SIM: Variantes explicitas ``` Cada boolean duplica os estados possiveis. 5 booleans = 32 combinacoes, maioria impossivel. --- ## Regra #2: Compound Components com Context (HIGH) ```tsx // Compound Components: context partilhado, composicao explicita const ComposerContext = createContext(null) function ComposerFrame({ children }: { children: React.ReactNode }) { return
{children}
} function ComposerInput() { const { state, actions: { update }, meta } = use(ComposerContext) return ( update(s => ({ ...s, input: text }))} /> ) } // Exportar como compound const Composer = { Frame: ComposerFrame, Input: ComposerInput, Submit: ComposerSubmit, Footer: ComposerFooter, } // Uso: composicao clara ``` --- ## Regra #3: State em Providers (HIGH) ```tsx // NAO: Estado preso dentro do componente function ForwardComposer() { const [state, setState] = useState(initialState) return ... } // SIM: State lifted para Provider function ForwardProvider({ children }: { children: React.ReactNode }) { const [state, setState] = useState(initialState) const submit = useForwardMessage() const inputRef = useRef(null) return ( {children} ) } // Qualquer componente dentro do Provider acede ao state function ForwardDialog() { return ( ) } function ForwardButton() { const { actions } = use(ComposerContext) return } ``` **Principio chave:** O boundary do Provider e o que importa, nao o nesting visual. --- ## Regra #4: Interface Generica para Dependency Injection (HIGH) ```tsx // 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 } } // Multiplos providers implementam a MESMA interface function ChannelProvider({ channelId, children }) { const { state, update, submit } = useGlobalChannel(channelId) return {children} } function ForwardProvider({ children }) { const [state, setState] = useState(initialState) return {children} } // 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) ```tsx // NAO: Render props (inflexivel)
} renderFooter={() => <>} /> // SIM: Children (composicao natural)
// Excepcao: render props quando parent passa dados } /> ``` --- ## Regra #6: React 19 APIs (MEDIUM) ```tsx // React 19: ref como prop normal (sem forwardRef) function Input({ ref, ...props }: Props & { ref?: React.Ref }) { return } // 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