React Advanced: Come disaccoppiare i tuoi componenti React nel modo giusto

Riccardo Tartaglia
weBeetle
Published in
5 min readOct 12, 2023

Hello, developer.

Il sogno di ogni sviluppatore è quello di poter scrivere meno codice e che sia possibilmente tutto riutilizzabile.

In React questo si traduce nel saper correttamente disaccoppiare la logica di un componente dalla sua presentazione.

Facile a dirsi vero?

In questo articolo ti mostro come disaccoppiare efficacemente i tuoi componenti in modo da rendere il tuo codice estremamente riutilizzabile.

Prima di iniziare, diamo un’occhiata al concetto fondamentale di “accoppiamento”.

Accopiamento (coupling)

In informatica, l’accoppiamento è un concetto che indica la dipendenza tra due o più componenti. Ad esempio, se un componente `A` dipende da un componente `B`, allora si dice che `A` è accoppiato con `B`.

L’accoppiamento è nemico del cambiamento, perché vincola fra loro cose che possono cambiare in parallelo.

Questo rende estremamente complesso modificare un singolo punto della tua applicazione poichè toccare un singolo componente significa apportare anomalie in svariati punti applicativi.

Devi dedicare del tempo a rintracciare tutte le parti che devono essere modificate, oppure dedicare del tempo a chiederti perché è andato tutto a rotoli.

Se vediamo un componente React come puro elemento di presentazione, possiamo dire che può essere accoppiato con molte cose:
- Logiche di business che determinano il suo comportamento (hooks, custom hooks, ecc.)
- Servizi esterni (API, database, ecc.)
- Altro componente React (ad esempio, un componente che si occupa di gestire lo stato di un form)

Questo accoppiamento stretto, quando subisce una modifica, può provocare effetti collaterali su altre parti del sistema, in modo totalmente imprevedibile.

Diamo un’occhiata a questo componente:

import { useCustomerHook } from './hooks';

const Customer = () => {
const { name, surname } = useCustomerHook();
return (
<div>
<p>{name}</p>
<p>{surname}</p>
</div>
);
};

Sembra tutto ok?

A primo impatto sembrerebbe di sì, in realtà abbiamo un problema: questo componente è accoppiato con il custom hook useCustomerHook, che si occupa di recuperare i dati del cliente da un servizio esterno e quindi questo non rende il nostro componente Customer un componente "puro" poiché dipende da una logica che non serve solo a presentare la propria UI.

Considerando che il custom hook useCustomerHook venga utilizzato anche in altri componenti, cosa ci aspetta se decidiamo di modificarlo?

Beh, ci aspetta un bel po' di lavoro, perché dovremo modificare tutti i componenti che lo usano e che sono accoppiati con esso.

Disaccoppiare la logica di un componente React

Riprendiamo l’esempio precedente, ho detto che il componente Customer è accoppiato con il custom hook useCustomerHook, che applica una logica di fetching per recuperare i dati di un cliente.

Cerchiamo di disaccoppiare la logica di questo componente, in modo da renderlo un componente puro di presentazione.

import { useCustomerHook } from './hooks';

const Customer = ({name, surname}) => {
return (
<div>
<p>{name}</p>
<p>{surname}</p>
</div>
);
};

const CustomerWrapper = () => {
const { name, surname } = useCustomerHook();
return <Customer name={name} surname={surname} />;
};

export default CustomerWrapper;

Ecco, ora il componente Customer è un componente puro di presentazione, perché non utilizza più useCustomerHook e applica solo logica di UI.

Ho utilizzato un componente wrapper per disaccoppiare la logica del componente Customer, questa tecnica è nota come Container Components e ci permette di modificare l’UI del nostro componente senza preoccuparci di "rompere" la logica dietro di esso.

Customer adesso deve solo preoccuparsi di mostrare le informazioni di presentazione; tutte le variabili di cui ha bisogno vengono passate come props e quindi possiamo innestarlo ovunque nel nostro codice senza preoccupazioni.

Ma non sono ancora soddisfatto per due motivi:

  1. Il componente CustomerWrapper è ancora accoppiato con il custom hook, quindi se decidessi di modificarlo, dovrei modificare anche il componente wrapper.
  2. Ho dovuto creare un componente in più, il CustomerWrapper, per disaccoppiare la logica quindi questa tecnica mi porta a scrivere un po' di codice in più.

Possiamo risolvere questi due problemi utilizzando la composizione.

Composizione

In informatica, la composizione è un concetto che indica la combinazione di due o più elementi per formarne uno nuovo.

Ad esempio, se abbiamo due funzioni `f` e `g`, possiamo comporle per formare una nuova funzione `h` che è la composizione di `f` e `g`.

const f = (x) => x + 1;
const g = (x) => x * 2;
const h = (x) => f(g(x));

Possiamo applicare lo stesso concetto anche ai custom hooks, infatti possiamo comporre due o più custom hooks per formarne uno nuovo.

const useCustomerHook = () => {
const { name, surname } = useCustomer();
const { age } = useCustomerAge();
return { name, surname, age };
};

In questo modo, il custom hook useCustomerHook è composto dai custom hooks useCustomer e useCustomerAge.

Usando la composizione possiamo disaccoppiare la logica di un componente React, senza dover creare un componente wrapper, per applicare la composizione utilizziamo per comodità la libreria react-hooks-compose.

Vediamo come possiamo applicare la composizione al nostro esempio:

import composeHooks from 'react-hooks-compose';
import { useCustomerHook } from './hooks';

const Customer = ({name, surname}) => {
return (
<div>
<p>{name}</p>
<p>{surname}</p>
</div>
);
};

export default composeHooks({useCustomerHook})(Customer);

Ecco, ora il componente Customer è un componente puro di presentazione, non accoppiato con nessun custom hook e applica solo logica di UI.

In più, non ho dovuto creare nessun componente per disaccoppiare la logica, anzi la composizione mi permette di creare un componente più pulito e leggibile.

Un altro punto di forza di questa tecnica sta nella facilità nel testare il componente Customer, infatti non dobbiamo preoccuparci di testare la logica di business, ma solo la logica di UI, oltre al fatto che possiamo testare i custom hooks separatamente.

Ultima chicca, vediamo cosa succede se decidessi di aggiungere un nuovo custom hook che aggiunga solo logica al componente Customer, ad esempio un custom hook che si occupa di loggare le informazioni del cliente:

import composeHooks from 'react-hooks-compose';
import { useCustomerHook, useLoggerHook } from './hooks';

const Customer = ({name, surname, age}) => {
return (
<div>
<p>{name}</p>
<p>{surname}</p>
</div>
);
};

export default composeHooks({
useCustomerHook,
useLoggerHook
})(Customer);

Ecco, ho aggiunto il custom hook useLoggerHook al componente Customer senza dover modificare il componente stesso, perchè il custom hook useLoggerHook è stato composto con il custom hook useCustomerHook.

In sintesi, abbiamo visto insieme come possiamo disaccoppiare la logica di un componente React tramite la composizione di hooks, in modo da renderlo un componente puro di presentazione.

L’arte della composizione degli hook ci offre un potente strumento per migliorare la modularità e la manutenibilità dei nostri componenti React.

Riusciamo a separare chiaramente la logica di business dalla presentazione, rendendo i nostri componenti più leggibili e facili da testare.

Questa metodologia promuove la riutilizzabilità del codice e la scalabilità delle applicazioni React, consentendo agli sviluppatori di concentrarsi sulla creazione di componenti più puliti e performanti.

Se hai suggerimenti o domande su questo argomento, non esitare a condividerli nei commenti qui sotto e, se l’articolo ti è stato utile, non dimenticare di condividerlo con i tuoi colleghi sviluppatori!

--

--

Riccardo Tartaglia
weBeetle

Fullstack Web Developer 🦄 Aiuto i professionisti e le aziende a comprendere i meccanismi della digitalizzazione.