25 aprile 2025

Design Pattern React che Ogni Sviluppatore Dovrebbe Conoscere

React ti offre un set di strumenti potenti per costruire interfacce utente, ma man mano che la tua app cresce, servono più di semplici hook e componenti: servono dei pattern.

In questo articolo esploreremo i design pattern specifici di React che ti aiuteranno a scrivere codice più pulito, manutenibile e scalabile. Non sono regole rigide: sono soluzioni riutilizzabili a problemi comuni nell’interfaccia utente.

Componenti Container e Presentational (Smart/Dumb)

Questo è uno dei pattern più antichi di React, ma ancora molto rilevante, specialmente quando si vogliono separare le responsabilità.

Esempio:

// Presentational
const UserCard = ({ user }) => <div>{user.name}</div>;

// Container
const UserCardContainer = () => {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetchUser().then(setUser);
  }, []);

  return user ? <UserCard user={user} /> : <p>Loading...</p>;
};

Quando usarlo: Ottimo per riusabilità e testabilità quando logica e UI sono strettamente collegate.

Componenti Compatti (Compound Components)

Questo pattern permette ai componenti di funzionare insieme come un gruppo, condividendo uno stato implicito. Pensa a <Select> con <Select.Option>.

const Tabs = ({ children }) => {
  const [activeIndex, setActiveIndex] = useState(0);

  return React.Children.map(children, (child, index) =>
    React.cloneElement(child, {
      isActive: index === activeIndex,
      onClick: () => setActiveIndex(index),
    }),
  );
};

const Tab = ({ isActive, onClick, children }) => (
  <button
    style={{ fontWeight: isActive ? "bold" : "normal" }}
    onClick={onClick}
  >
    {children}
  </button>
);

Quando usarlo: Ideale per componenti coesi che dipendono da uno stato interno condiviso (menu, tab, modali, ecc.).

Custom Hook

I custom hook sono semplicemente funzioni che incapsulano logica, permettendone il riutilizzo in più componenti.

function useFetch(url: string) {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch(url).then(res => res.json()).then(setData);
  }, [url]);

  return data;
}

Quando usarlo: Ogni volta che ti ritrovi a copiare la stessa logica tra più componenti.

Render Props (Meno Comune Ora, ma Utile)

Questo pattern ti permette di condividere codice usando una funzione come figlio.

const MouseTracker = ({ children }) => {
  const [coords, setCoords] = useState({ x: 0, y: 0 });

  const handleMouseMove = (e) => setCoords({ x: e.clientX, y: e.clientY });

  return <div onMouseMove={handleMouseMove}>{children(coords)}</div>;
};

// Usage
<MouseTracker>
  {({ x, y }) => (
    <p>
      Mouse is at {x}, {y}
    </p>
  )}
</MouseTracker>;

Quando usarlo: Per comportamenti dinamici e riutilizzabili (ma oggi spesso sostituito dai custom hook).

Componenti Controllati vs. Non Controllati

Un classico pattern UI:

// Controlled
<input value={value} onChange={e => setValue(e.target.value)} />

// Uncontrolled
<input ref={inputRef} />

Quando usarlo: Componenti controllati per precisione e validazione, componenti non controllati per performance o casi semplici.

Pattern State Reducer

Questo pattern consente ai consumatori del tuo componente di controllare le transizioni di stato.

function useToggle({ reducer }) {
  const [state, dispatch] = useReducer(reducer, { on: false });

  const toggle = () => dispatch({ type: "toggle" });

  return [state.on, toggle];
}

Quando usarlo: Utile per costruire componenti e librerie riutilizzabili che necessitano di un comportamento personalizzabile.

Hook + Context = Condivisione di Stato Scalabile

Combina React Context con custom hook per la gestione di stato globale o condiviso.

const ThemeContext = createContext(null);

export const useTheme = () => useContext(ThemeContext);

export const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState("light");

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

Quando usarlo: Per temi globali, autenticazione, impostazioni della lingua, ecc.

Conclusione

Questi pattern specifici per React non sono solo teorici: sono modi collaudati per gestire la complessità man mano che la tua interfaccia utente cresce. Imparali, adattali e sappi quando infrangerli.