eduardweb.
Hooks & PatternsAvansat#typescript#react#frontend#design-patterns

Adio componente monolit: Cum scrii un Accordion flexibil cu Compound Components

De Elisabeta Stan, 4 iul. 2026 · 11 vizualizări · 3 like-uri

Postat 4 iul. 2026
typescript
import React, { createContext, useContext, useState } from "react";

const AccordionContext = createContext<{
  openValue: string | null;
  toggle: (value: string) => void;
} | null>(null);

export function Accordion({ children }: { children: React.ReactNode }) {
  const [openValue, setOpenValue] = useState<string | null>(null);
  const toggle = (value: string) => setOpenValue(openValue === value ? null : value);
  return (
    <AccordionContext.Provider value={{ openValue, toggle }}>
      <div className="border border-slate-200 rounded-lg">{children}</div>
    </AccordionContext.Provider>
  );
}

export function AccordionItem({ value, children }: { value: string; children: React.ReactNode }) {
  return <div className="border-b last:border-0">{children}</div>;
}

export function AccordionTrigger({ value, children }: { value: string; children: React.ReactNode }) {
  const context = useContext(AccordionContext);
  if (!context) throw new Error("Trigger must be used inside Accordion");
  return (
    <button 
      onClick={() => context.toggle(value)}
      className="w-full flex justify-between p-4 font-medium hover:bg-slate-50"
    >
      {children}
    </button>
  );
}

M-am lovit recent de o rescriere de design system la un proiect SaaS cu vreo 12.000 de utilizatori activi și am realizat din nou cât de mult urăsc componentele monolit. Știi și tu genul ăla de <Accordion title="FAQ" content="..." isOpen={true} /> care devine imposibil de întreținut când designerul vrea o iconiță în stânga, dar doar la al treilea rând. Soluția curată, pe care o folosesc și cei de la shadcn sau Radix, este pattern-ul de compound components.

De ce explodează componentele monolit?

La început, o componentă simplă pare o idee bună. Îi pui trei prop-uri și ai terminat treaba. Dar după două sprinturi, marketingul vrea un badge lângă titlu, echipa de SEO vrea taguri <h3> în loc de <div>, iar tu ajungi să adaugi prop-uri ca renderHeader, headerClassName, hideChevron și alte improvizații.

Codul devine un coșmar greu de citit și de testat. Compound components rezolvă asta prin delegarea controlului către cel care consumă componenta. Practic, spargi componenta în bucăți atomice care comunică între ele în spate printr-un context React, lăsând layout-ul complet liber.

Anatomia unui Accordion flexibil

Ideea e simplă: avem un părinte care ține starea (ce element este deschis) și copii care se folosesc de această stare. Consumatorul poate să pună orice div-uri de styling între ele, fără să spargă logica internă.

Gândește-te la flexibilitatea pe care o câștigi. Vrei să pui o iconiță de alertă doar lângă anumite headere de accordion? Pur și simplu o adaugi în interiorul <AccordionTrigger>. Vrei ca un anumit rând să aibă un fundal roșu pentru că e o eroare critică? Trântești o clasă de CSS direct pe acel <AccordionItem>. Nu trebuie să rescrii nicio linie de logică JS din componenta de bază. Totul se rezolvă prin compoziție declarativă pură.

Trade-off-ul de care nu vorbește nimeni

Hai să fim sinceri, niciun pattern nu e glonțul de argint. Compound components are un dezavantaj clar: mult boilerplate. Dacă ai nevoie de un accordion simplu într-un sidebar obscur și vrei doar să trântești rapid două rânduri de text, să scrii patru componente imbricate pare o muncă de Sisif.

În plus, dacă un coleg mai junior uită să pună <AccordionItem> în interiorul <Accordion> și îl trântește direct în DOM, React va arunca o eroare urâtă la runtime pentru că useContext va returna undefined. De aceea, e obligatoriu să pui o verificare în custom hook-ul tău și să arunci o eroare explicită de tipul "Accordion component must be used within an AccordionProvider".

La proiectul de care vă ziceam, după ce am trecut FAQ-ul și secțiunea de setări pe acest pattern, am redus tichetele de bug-uri pe layout cu aproape 40%. Designerii își pot face de cap cu Tailwind direct în pagină, fără ca eu să mai ating vreodată codul React.

Voi ce abordare preferați pentru componentele de UI complexe? Mergeți pe flexibilitate maximă cu compound components sau preferați varianta rapidă, chiar dacă e mai rigidă?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.