Controlled vs Uncontrolled
Uma decisão importante que afeta bastante na qualidade do seu código é determinar onde o estado de um componente deve residir, e esta decisão impacta diretamente a flexibilidade, reusabilidade e complexidade.
Um exemplo clássico desta decisão é a implementação de um modal: deve ele gerenciar seu próprio estado de visibilidade ou delegar este controle ao componente pai?
Componentes Controlados: Delegando o Controle
Em um componente controlado, o estado é gerenciado externamente pelo componente pai.
Esta abordagem oferece maior flexibilidade e permite que o componente pai coordene múltiplos estados relacionados.
Nessa abordagem, podemos dizer que a flexibilidade do Modal é alta, mas a
responsabilidade sobre ele também é, pois, o componente pai precisa manter a lógica necessária para abrir e fechar o modal.
// Exemplo de Modal controlado: export function Modal({ isOpen, onChangeIsOpen, children }) { // O componente recebe o estado como prop e delega mudanças através de callbacks return ( <dialog open={isOpen} onClose={() => onChangeIsOpen(false)} > {children} </dialog> ); } // Exemplo de uso: export function App() { const [isOpen, setIsOpen] = useState(false); return ( <div> <Modal isOpen={isOpen} onChangeIsOpen={setIsOpen}> <p>Greetings, one and all!</p> <button onClick={() => setIsOpen(false)}>OK</button> </Modal> <button onClick={() => setIsOpen(true)}>Open Modal</button> </div> ); }
Componentes Não Controlados: Autonomia Interna
Por outro lado, componentes não controlados mantêm e gerenciam seu próprio estado internamente. Esta abordagem reduz a complexidade do componente pai e é ideal quando o comportamento do componente pode ser encapsulado de forma independente.
// Exemplo de modal não controlado: export function Modal({ children, onOpenChange }) { const [isOpen, setIsOpen] = useState(false); const handleOpenChange = (newState) => { setIsOpen(newState); onOpenChange?.(newState); // Opcional: notifica o pai sobre mudanças }; if (!isOpen) { return ( <button onClick={() => handleOpenChange(true)}>Open Modal</button> ); } return ( <dialog open onClose={() => handleOpenChange(false)} > {children} <button onClick={() => handleOpenChange(false)}>OK</button> </dialog> ); } // App.tsx function App() { return ( <Modal onOpenChange={(isOpen) => console.log('Modal state:', isOpen)}> <p>Greetings, one and all!</p> </Modal> ); }
Esta decisão de design influencia diretamente a manutenibilidade e reusabilidade do seu código.
Use componentes controlados quando:
- O componente pai precisa coordenar múltiplos estados relacionados
- Você precisa de controle preciso sobre o comportamento do componente
- O estado do componente precisa ser sincronizado com outras partes da aplicação
Use componentes não controlados quando:
- O comportamento do componente é independente do resto da aplicação
- Você quer reduzir a complexidade do componente pai
- O componente pode funcionar de forma autônoma
Top comments (0)