Articolo originale: React Best Practices – Tips for Writing Better React Code in 2022

Due anni fa, ho iniziato a imparare e a usare React, e oggi continuo a usarlo per il mio lavoro di sviluppatore software e per progetti personali.

In questo tempo ho incontrato un sacco ti problemi "tipici". Così ho cercato in giro e ho trovato alcune buone pratiche che ho integrato nel mio flusso di lavoro e che hanno reso più semplice la mia vita e quella dei miei colleghi.

Durante questo percorso ho anche affrontato sfide che all'inizio non ho risolto al meglio e a cui voglio approcciarmi in un modo migliore in futuro.

Questa è la ragione per cui ho scritto questa guida. A cui penso come a una raccolta di suggerimenti che avrei voluto dare a me stesso due anni fa, quando ho iniziato.

Indice:

Innanzitutto, conoscerai le tre sfide principali che ogni sviluppatore React deve affrontare. Questo è importante perché quando sei cosciente delle potenziali sfide, capirai le ragioni alla base di queste buone pratiche in modo più profondo. Avere questa mentalità dall'inizio può essere d'aiuto anche quando stai progettando dei componenti o organizzando un progetto.

Dopo questo primo importante passo, ti parlerò di tre buone pratiche. Sono un mix di suggerimenti teorici e pratici con esempi di codice. Proverò a minimizzare i problemi in stile hello world e arrivare a ciò che ho visto nel mondo reale.

Le tre sfide che ogni sviluppatore React affronta

christian-erfurt-sxQz2VfoFBE-unsplash

Durante i miei due anni di uso quotidiano di React, ho individuato tre sfide principali che uno sviluppatore React deve affrontare per realizzare le proprie app. Ignorare queste difficoltà potrebbe portare a momenti difficili che nuocciono alla crescita dell'app.

Quindi, tieni a mente questi problemi quando orchestri la tua app, perché ti risparmieranno tempo ed energie.

⚙️ Manutenibilità

Questo va a braccetto con la riusabilità. All'inizio, quando l'applicazione e i componenti sono molto leggeri, è semplice fare manutenzione. Ma una volta che le esigenze iniziano a crescere, i componenti tendono a diventare molto complessi e perciò più difficili da gestire.

Spesso ho visto un componente con molti casi diversi, ognuno che rappresenta un risultato diverso. Il JSX è inondato da rendering condizionale (operatori ternari e semplici operatori &&), classname applicati in modo condizionale o componenti che usano un'istruzione switch enorme. Ci sono molti possibili valori di prop e stati, ognuno responsabile di un risultato diverso.

Secondo me, non c'è niente di sbagliato in queste tecniche in quanto tali. Ma credo che tutti dovrebbero pensare a cosa fare quando un componente inizia a diventare poco manutenibile e quando queste tecniche vengono usate eccessivamente. Impareremo come controllare questo aspetto più avanti in questo articolo.

Il problema (e ne sono colpevole anch'io) è che maggiori sono la complessità e i diversi risultati di un componente (polimorfismo), più questo risulta difficile da gestire.

A essere onesto, la causa principale è spesso la pigrizia, esperienza insufficiente o mancanza di tempo per fare il refactoring di un componente in modo adeguato, così da renderlo più gestibile e pulito.

Un altro fattore chiave che ho visto è l'insufficienza o la mancanza di test. Lo so, non è un tipo di lavoro che molti sviluppatori amano, ma può davvero essere d'aiuto sul lungo termine.

🧠 Solida conoscenza di React

Un'altra causa principale dei problemi degli sviluppatori React è una scarsa comprensione del funzionamento di base di React. Ci sono passato anch'io.

Ho visto molte persone saltare troppo velocemente in concetti intermedi o avanzati senza avere basi solide. Ma questo non è specifico di React, bensì è un problema generale nella programmazione.

Non avere delle conoscenze solide di React può anche causare problemi a te come sviluppatore. Ricordo i mal di testa avuti quando volevo usare diversi cicli di vita di componenti senza sapere effettivamente come farlo. Quindi ho dovuto fare qualche passo indietro e poi approfondire l'argomento.

Dato che ritengo che sia uno degli aspetti più importanti, gli ho dedicato un intero capitolo all'interno di questo articolo.

📈 Scalabilità

Questa sfida va a braccetto con la manutenibilità. Non è specifica per React, ma si applica in genere ai software.

Ho imparato che per realizzare dei software eccellenti non contano solo esperienza utente, schemi di codice puliti o un'architettura ingegnosa. Per me, la qualità di un software aumenta o diminuisce in base alla possibilità di essere scalato.

Molte cose entrano in gioco nell'aumentare la scalabilità di un software. In questo articolo troverai dei suggerimenti utili a questo riguardo.

Credo che tenendo a mente la manutenibilità e la scalabilità quando orchestri i tuoi componenti e organizzi la struttura del progetto, sia meno probabile che tu finisca per avere un codice sorgente incasinato che ha bisogno di un refactoring.

Come imparare React

Bene, adesso affrontiamo approfonditamente delle buone pratiche per imparare a usare React.

Imparare gli elementi fondamentali di React

brett-jordan-Lzfxzip-pNI-unsplash

Come abbiamo già discusso brevemente, conoscere gli elementi fondamentali non è importante soltanto per React, ma anche per qualsiasi altra tecnologia o linguaggio di programmazione. Non puoi costruire un grattacielo su delle fondazioni di sabbia e aspettarti che sia stabile.

Potrebbe sembrare ovvio per molti di voi, ma ho visto sviluppatori saltare direttamente a concetti di React intermedi o avanzati senza comprenderne davvero le basi.

Questo è vero anche per JavaScript in generale. Credo fortemente che imparare React non ha senso se non hai delle solide basi di JavaScript vanilla (JS senza framework o librerie aggiuntive).

Se ti sembra qualcosa di familiare e stai pensando di imparare a usare React ma non ti senti ancora a tuo agio con JavaScript, dedica prima del tempo a rafforzare le tue conoscenze di JavaScript. In futuro, ti risparmierà un sacco di mal di testa e di tempo.

Ecco una guida utile per i concetti di JavaScript più importanti che devi conoscere prima di passare a React.

Ma, secondo me, conoscere soltanto le basi non è sufficiente. È in qualche modo obbligatorio conoscere il funzionamento di React dietro le quinte. Se vuoi diventare un bravo sviluppatore React (e immagino sia così, visto che stai leggendo questo articolo), devi conoscere gli strumenti che stai utilizzando. È un bene per te in quanto sviluppatore e per i tuoi clienti.

Da un lato può salvarti da un sacco di tempo dedicato al debugging della tua app. Inoltre ti rende più efficiente perché non devi fare dei passi indietro e imparare di nuovo le basi, ancora e ancora. In pratica, sai quello di cui stai parlando.

Naturalmente, non puoi sapere tutto e non dovresti stressarti per questo. Imparerai sempre di più quando ti troverai ad affrontare problemi pratici e costruire altri progetti, ma con delle conoscenze solide parti ben equipaggiato.

Direi che ha senso, ma potresti chiederti cosa hai bisogno di sapere esattamente per avere delle basi solide di React.

Lo stretto indispensabile è costituito da tutti gli argomenti presenti nei Concetti Chiave della documentazione sul sito ufficiale di React.

Un altro argomento con cui dovresti avere familiarità è quello sugli Hooks perché sono diventati una convenzione e sono usati ovunque, specialmente in pacchetti React di terze parti.

Di certo potresti usare più di frequente useState e useEffect, ma è essenziale anche conoscerne altri come useMemo, useCallback o useRef.

C'è anche un altro capitolo chiamato Guide Avanzate che non considererei obbligatorio all'inizio, ma di cui raccomanderei vivamente di apprendere i concetti durante il tuo viaggio in React.

Come sempre, è spesso più semplice comprendere argomenti avanzati quando possiedi già un po' di esperienza pratica. Ma più cose apprendi da subito, meglio è.

Naturalmente non devi limitarti a seguire soltanto la documentazione di React. Anche seguire corsi online che trattano gli elementi essenziali, guardare tutorial o leggere blog fa parte della costruzione di solide basi. Prova quello che funziona meglio per te.

Se dovessi indicare quali sono i concetti più importanti da conoscere indispensabilmente, sceglierei questi:

  • cos'è uno "stato"?
  • up e down di classe e componenti funzionali
  • cosa i ri-rendering dei componenti e come funzionano?
  • come attivare i ri-rendering
  • differenti cicli di vita dei componenti e come interagire con essi
  • DOM virtuale
  • Benefici di CSR (Client Side Rendering) e SSR (Server Side Rendering) in generale e in React
  • Componenti controllati vs non controllati
  • Spostamento di stato
  • almeno una tecnologia di gestione dello stato globale (Context API, Redux/Toolkit, Recoil)
  • Pattern di componenti (soprattutto come scegliere il giusto pattern)

Come realizzare dei componenti React puliti, prestanti e manutenibili

wesley-tingey-mvLyHPRGLCs-unsplash

Lo so – questo è il sogno di ogni programmatore (o almeno spero sia così). E per me, questa abilità distingue un buon programmatore da un ottimo programmatore. La parte divertente è che non è mai tutto completo perché c'è sempre qualcosa di nuovo da imparare e migliorare.

Seguire le buone pratiche non renderà le cose più semplici solo a te, ma anche ai tuoi colleghi. Ho visto team di sviluppo che hanno creato una guida di stile in cui hanno definito dei capisaldi su come scrivere il codice. Un'idea che apprezzo molto.

Alcuni di essi sono:

  • usare componenti funzionali (come le funzioni freccia)
  • non usare stili inline
  • mantenere una struttura di importazione appropriata (prima import di terze parti --> poi import interni)
  • formattare il codice prima di un commit

E via dicendo.

Naturalmente è possibile essere molti dettagliati a questo riguardo. Questo dipende dal tuo team. Personalmente, non amo le guide estremamente dettagliate perché penso che uno sviluppatore esperto dovrebbe avere una certa libertà e non dovrebbe essere troppo vincolato.

In ogni caso, una guida di stile è un buon modo per definire e mantenere delle buone pratiche e assicurarsi che il team sia allineato per quanto riguarda le questioni importanti. Credo che questo favorisca incredibilmente il lavoro di squadra e i risultati.

Diamo un'occhiata alle buone pratiche che riguardano la creazione di componenti puliti, prestanti e manutenibili. Mettiti comodo, tieni a portata qualcosa per prendere appunti e buon divertimento!

📁 Creare una buona struttura di cartelle

Organizzare i tuoi file e cartelle all'interno di un'app React è obbligatorio per la manutenibilità e la scalabilità.

Una buona struttura di cartella dipende dalla dimensione dell'applicazione e del team, quindi non esiste una soluzione generale per questo. Soprattutto perché si tratta di un argomento molto discusso e dipende anche dalle preferenze personali.

Col tempo, però, si sono sviluppate alcune buone pratiche per le varie dimensioni di un'applicazione.

Questo ottimo post (risorsa in inglese) passa in rassegna cinque diverse dimensioni di un'app e introduce buone idee su come organizzare file e cartelle. Avere queste cose in mente quando pianifichi o avvii la tua applicazione può fare una differenza enorme sul lungo termine.

Non sovra-ingegnerizzarla, ma fai del tuo meglio per mantenere una strutturata appropriata che si adatti alla dimensione dell'applicazione e del team.

👇 Mantenere un ordine di import strutturato

Se hai già un po' di esperienza con React, potresti aver visto file rigonfi di istruzioni import. Potrebbero anche essere mescolati a import esterni da pacchetti di terze parti e import interni come altri componenti, funzioni di utilità, stili e molto altro.

Un esempio reale (tagliato):

import React, { useState, useEffect, useCallback } from "react";
import Typography from "@material-ui/core/Typography";
import Divider from "@material-ui/core/Divider";
import Title from "../components/Title";
import Navigation from "../components/Navigation";
import DialogActions from "@material-ui/core/DialogActions"
import { getServiceURL } from '../../utils/getServiceURL";
import Grid from "@material-ui/core/Grid";
import Paragraph from "../components/Paragprah";
import { sectionTitleEnum } from "../../constants";
import { useSelector, useDispatch } from "react-redux";
import Box from "@material-ui/core/Box";
import axios from 'axios';
import { DatePicker } from "@material-ui/pickers";
import { Formik } from "formik";
import CustomButton from "../components/CustomButton";
...
In realtà gli import occupano 55 righe.

Probabilmente ti sei accorto del problema qui. È difficile distinguere quali sono gli import di terze parti e quelli locali (interni). Non sono raggruppati e sembrano essere ovunque.

Una versione migliore:

import React, { useState, useEffect, useCallback } from "react";
import { useSelector, useDispatch } from "react-redux";
import { Formik } from "formik";
import axios from 'axios';
import Typography from "@material-ui/core/Typography";
import Divider from "@material-ui/core/Divider";
import Box from "@material-ui/core/Box";
import DialogActions from "@material-ui/core/DialogActions";
import Grid from "@material-ui/core/Grid";
import { DatePicker } from "@material-ui/pickers";

import { getServiceURL } from '../../utils/getServiceURL";
import { sectionTitleEnum } from "../../constants";
import CustomButton from "../components/CustomButton";
import Title from "../components/Title";
import Navigation from "../components/Navigation";
import Paragraph from "../components/Paragraph";
...

La struttura è più chiara ed è molto semplice distinguere dove si trovano gli import esterni e interni. Certamente è possibile ottimizzare questa disposizione se stai usando più import (nel caso sia possibile :) ). Questo ti permette di importare tutti i componenti derivanti da material-ui tutti in una sola riga.

Ho visto altri sviluppatori a cui piace dividere la struttura degli import in tre parti:

Integrati (come 'react') --> Esterni (moduli di node di terze parti) --> Interni.

Puoi gestire tutto da solo oppure lasciar fare il lavoro a un linter.

📔 Imparare diversi schemi di componenti

Per fare in modo di non finire con del codice ingestibile e non scalabile, è essenziale imparare diversi pattern di componenti man mano che acquisisci esperienza con React.

Ma non è tutto. Conoscere i diversi pattern è un buon punto di partenza, ma l'aspetto più importante è sapere quando usare ognuno di essi per uno specifico problema.

Ogni pattern ha il suo scopo. Ad esempio, il compound component pattern evita il prop-drilling non necessario di molti livelli di componenti. Quindi, la prossima volta che inizi a passare delle prop attraverso 5 livelli di componenti per raggiungere alla fine il componente interessato dalla prop, comincerai a dirigere i componenti diversamente.

Una breve nota sul prop-drilling, visto che ho avuto molte discussioni in passato. Ci sono molte opinioni sul fatto che sia una cosa positiva o no. A me piace provare a pensare a un modo/pattern diverso se inizio a passare prop attraverso più di due livelli di componenti.

Ciò ti rende più efficiente come sviluppatore e fa sì che i componenti che scrivi siano più manutenibili e scalabili. Avere questi pattern nel tuo arsenale ti distingue dagli altri sviluppatori React. Ti consiglio vivamente di fare le tue ricerche, ma questo corso su Udemy mi ha aiutato molto.

🔒Usa un linter e segui le sue regole

Un linter non ti aiuta soltanto a mantenere un ordire di import delle dipendenze distinguibile, ti aiuta a scrivere un codice migliore in generale.

Quando utilizzi create-react-app, ESLint è già configurato, ma puoi anche impostarlo completamente da solo o estendere un set di regole preconfigurato.

In pratica, un linter osserva il codice JavaScript che stai scrivendo e ti fa notare gli errori di cui ti accorgeresti probabilmente eseguendo il codice. Ci ho messo un po' per apprezzare davvero l'utilità di un linter, ma oggi non posso immaginare di lavorare senza.

Avere un linter è una cosa, ma seguire le sue regole è un'altra. Naturalmente puoi disabilitarlo, sia per una specifica riga di codice che per un intero file. Potrebbero esserci casi in cui ha senso farlo, ma per esperienza sono piuttosto rari.

Un altro grande vantaggio è che puoi anche regolare il controllo dello stile. È particolarmente utile per il lavoro in gruppo. Una volta che si è d'accordo su certe convenzioni su come scrivere e formattare il codice, è facile combinare ESLint con qualcosa simile a JSPrettify.

🧪 Testare il codice

Lo so, i test non sono la tua attività da sviluppatore preferita. Per me era così. All'inizio sembra un compito non necessario e fastidioso, e sul breve termine può essere vero. Ma sul lungo periodo – e quando un'applicazione cresce – è vitale.

Per me, i test sono diventati una pratica che mi assicura che sto facendo il mio lavoro in modo più professionale, creando software di migliore qualità.

In pratica, non c'è nulla di sbagliato con i test manuali effettuati da una persona, che non dovrebbero essere evitati completamente. Ma immagina di star integrando una nuova funzionalità e di voler essere sicuro che tutto funzioni a dovere. È un'attività che richiede tempo e favorisce l'errore umano.

Mentre stai scrivendo i test ti trovi già nel processo mentale di organizzare il codice in modo che passi i test. Per me è molto utile perché mi aiuta a capire quali insidie possono sorgere e a tenere un occhio su di esse.

Non ti tuffi direttamente a scrivere il tuo codice (cosa che non raccomando mai), ma pensi prima all'obiettivo.

Ad esempio, "Cosa dovrebbe fare uno specifico componente? Quali sono i casi limite rilevanti che devo testare? Posso rendere il componente più semplice in modo che abbia un solo scopo? ..."

Avere una visione del codice che stai per scrivere ti aiuta anche a mantenere alta l'attenzione per soddisfarla.

I test possono anche fungere da documentazione, perché per un nuovo sviluppatore non avvezzo al codebase può essere molto d'aiuto a capire le diverse parti del software e come ci si aspetta che funzionino.

Quindi non evitare i test perché sembrano un lavoro extra. La verità è che puoi risparmiare del tempo in futuro se li imposti appropriatamente.

Dai un'occhiata al capitolo sui test della documentazione di React, segui qualche tutorial sui test in React e inizia a scrivere una piccola applicazione TDD (test-driven development) o implementa dei test in una app su cui stai lavorando al momento.

🧰 Integrare Typescript (o usare almeno le prop di default e prop type)

Ricordo il mio primo progetto in React come sviluppatore software in cui il nostro gruppo ricevette un progetto in pratica già scritto da un'altra azienda. Abbiamo dovuto creare il progetto del client su di esso e Typescript era già integrato.

Fino a quel momento, io e i miei colleghi non avevamo molta esperienza con Typescript, dato che avevamo tutti un background in JavaScript vanilla.

Dopo poche settimane passate a lavorare sul progetto, abbiamo sentito che TypeScript non era un beneficio, ma un ostacolo che bloccava il nostro lavoro. Non stavamo proprio sfruttando i suoi vantaggi, perché avevamo definito tutto con tipo any per bloccare gli avvertimenti di Typescript.

Questo ci portò a decidere di rimuovere Typescript dal progetto e lavorare sul nostro terreno con JavaScript. All'inizio andò bene, ma man mano che il progetto diventava più complesso, iniziarono a venir fuori errori di tipo. Ciò ci fece dubitare della nostra decisione di sbarazzarci completamente di Typescript. Ma queste cose possono accadere e danno una preziosa esperienza per il futuro.

Queste circostanze mi hanno portato a dare a Typescript un'altra opportunità, così l'ho imparato nel mio tempo libero. Dopo avervi costruito alcuni progetti secondari, non potevo immaginare più la vita senza Typescript.

Usare Typescript ha molti lati positivi, come il controllo statico dei tipi, un migliore completamento del codice nel tuo IDE (intellisense), un'esperienza di sviluppo migliorata e l'individuazione di errori di tipo mentre scrivi il codice – tanto per nominarne alcuni.

D'altra parte, ci sono anche delle sfide, perché se non vieni da un background con linguaggi fortemente tipizzati (come Java o C#) potrebbe essere più difficile da afferrare all'inizio.

Ma posso dire che vale davvero la pena di impararlo e integrarlo. Ecco un bell'articolo (risorsa in inglese) che può aiutarti ad avere una panoramica dei pro e dei contro dell'uso di Typescript in una applicazione React. E qui c'è un tutorial (risorsa in inglese) su come programmare le tue app React in Typescript.

Ci potrebbero essere ragioni per cui non vuoi usare Typescript nella tua app React, va bene. Ma consiglio almeno di usare i prop-type e le default-prop per i tuoi componenti per essere sicuro di non fare confusione con le prop.

💎 Usare lazy-loading / code splitting

Se hai passato un po' di tempo nell'universo JavaScript e React, sei probabilmente incappato nell'"impacchettamento" (bundling). Per quelli che stanno sentendo questo termine per la prima volta, vediamo cosa dice la documentazione ufficiale di React:

Molte applicazioni React hanno i loro file “impacchettati” usando strumenti come Webpack, Rollup oppure Browserify. Il processo di “impacchettamento” avviene prendendo tutti i file importati e unendoli tutti in un unico file, chiamato “bundle”. Questo file può essere poi incluso all’interno della pagina web per caricare l’intera applicazione tutta in una volta.

In pratica, si tratta di un'ottima tecnica, ma con la crescita di una app si presenta una sfida. Il bundle inizia a crescere a sua volta. Soprattutto quando stai usando delle grandi librerie di terze parti come three.js.

L'insidia è che questo bundle ha bisogno di essere sempre caricato completamente, anche quando l'utente ha bisogno solo di una frazione del codice. Questo porta a problemi di prestazione perché può volerci un tempo eccessivamente lungo per caricare l'app.

Per evitarlo, esiste una tecnica chiamata code splitting in cui dividi i bundle nelle porzioni di codice di cui ha bisogno l'utente. Questa tecnica è supportata dalla maggior parte degli strumenti comuni come Webpack, Rollup, e Browserify. Il grande beneficio è che puoi creare più bundle e caricarli dinamicamente.

Dividere i bundle ti aiuta a caricare tramite lazy loading solo le cose necessarie all'utente.

Immagina di andare in un negozio di alimentari e di voler comprare solo delle banane, delle mele e del pane. In questo caso, non stai comprando tutta la merce a disposizione nel negozio, sei interessato solo a una porzione della merce a disposizione. Quindi? Compreresti tutto? Ci vorrebbe molto più tempo e sarebbe parecchio più costoso.

Credo sia importante essere consapevoli delle potenziali sfide che possono sorgere man mano che una app cresce e ci sono alcune tecniche a disposizione per liberarsene. Per approfondire, dai un'occhiata alla documentazione di React.

🗄️ Estrarre logica riutilizzabile in hook personalizzati

Secondo la documentazione di React:

Gli hook ci consentono di riutilizzare la logica stateful senza dover cambiare la gerarchia dei componenti.

In pratica, si tratta di una soluzione migliore alle tecniche che erano usate precedentemente in combinazione con i componenti di classe. Se ti interessi di programmazione da un po', potresti ricordarti l'utilizzo di componenti di ordine superiore o render prop.

Ogni volta che ti trovi in una situazione in cui devi riutilizzare la stessa logica stateful già utilizzata in un altro componente funzionale, è un ottimo momento per creare un hook personalizzato. Al suo interno puoi incapsulare la logica e poi ti basterà chiamare l'hook come una funzione all'interno dei componenti.

Diamo un'occhiata a un esempio veloce in cui dobbiamo aggiornare la nostra interfaccia utente in base alla dimensione dello schermo e vogliamo tenere traccia della dimensione attuale della finestra ridimensionando manualmente la finestra del browser.

const ScreenDimensions = () => {
  const [windowSize, setWindowSize] = useState({
    width: undefined,
    height: undefined,
  });
  
  useEffect(() => {
    function handleResize() {
      setWindowSize({
        width: window.innerWidth,
        height: window.innerHeight,
      });
    }
    window.addEventListener('resize', handleResize);
    handleResize();
    return () => window.removeEventListener('resize', handleResize);
  }, []);
  
  return (
  	<>
    	<p>Current screen width: {windowSize.width}</p>
        <p>Current screen height: {windowSize.height}</p>
    </>
  )
}
Grazie a: https://usehooks.com/useWindowSize/

Come puoi vedere, la soluzione è piuttosto diretta e non c'è nulla di sbagliato a definirlo così.

Adesso arriva la parte complessa. Immagina di voler usare la stessa logica in un altro componente, in cui renderizzare un'interfaccia utente diversa (una per smartphone e una per desktop) in base alla dimensione dello schermo.

Naturalmente, potremmo copiare la logica, incollarla, e il gioco è fatto. Ma questa non è una buona pratica, come potresti sapere dal principio DRY (Don't Repeat Yourself, ovvero "non ripeterti").

Se volessimo regolare la logica, dovremmo farlo in entrambi i componenti. E quando incolliamo la logica in ancora più componenti, diventa meno manutenibile e più favorevole agli errori.

Quindi, che faresti normalmente in un progetto JavaScript? Probabilmente definiresti una funzione che incapsula la logica e che può essere usata in molti posti diversi. È esattamente ciò che otteniamo con gli hook. Non sono nulla di più che funzioni JavaScript con alcune specialità di React perché stanno usando gli hook di React.

Vediamo come sarà il nostro hook personalizzato:

const useWindowSize = () => {
  const [windowSize, setWindowSize] = useState({
    width: undefined,
    height: undefined,
  });
  
  useEffect(() => {
    function handleResize() {
      setWindowSize({
        width: window.innerWidth,
        height: window.innerHeight,
      });
    }
    window.addEventListener('resize', handleResize);
    handleResize();
    return () => window.removeEventListener('resize', handleResize);
  }, []);
  
  return windowSize;
}

Adesso chiamiamolo semplicemente all'interno del componente ScreenDimensions:

const ScreenDimensions = () => {
  const windowSize = useWindowSize()
  
  return (
  	<>
    	<p>Current screen width: {windowSize.width}</p>
        <p>Current screen height: {windowSize.height}</p>
    </>
  )
}

Ciò ci consente di chiamare semplicemente l'hook personalizzato in qualsiasi altro componente e salvare il valore di ritorno (che è la dimensione attuale della finestra) in una variabile che possiamo usare all'interno del componente.

const ResponsiveView = () => {
  const windowSize = useWindowSize()
  
  return (
  	<>
    	{windowSize.width <= 960 ? (
          <SmartphoneView />
        ) : (
          <DesktopView />	
        )}
    </>
  )
}

🖥️ Gestire efficacemente gli errori

La gestione efficace degli errori è spesso trascurata e sottovalutata da molti sviluppatori. Come altre buone pratiche, sembra essere un aspetto secondario all'inizio. Vuoi far funzionare il codice e non vuoi "sprecare" tempo pensando agli errori.

Ma una volta che sei diventato più esperto e sei stato in situazioni spiacevoli in cui una gestione migliore degli errori ti avrebbe risparmiato molte energie (e del tempo prezioso, naturalmente), realizzi che sul lungo termine è obbligatorio avere una valida strategia di gestione degli errori nella tua applicazione. Soprattutto quando l'applicazione è distribuita in produzione.

Ma cosa significa esattamente gestione degli errori nel mondo di React? Ci sono diverse parti che hanno un ruolo. Una per individuare gli errori, un'altra per gestire l'interfaccia utente di conseguenza e l'ultima per registrarli appropriatamente.

React Error Boundary

Questo è un componente di classe personalizzato usato come wrapper per l'intera applicazione. Naturalmente, puoi avvolgere il componente ErrorBoundary anche attorno a componenti più in profondità nell'albero dei componenti per renderizzare UI più specifiche, ad esempio. Fondamentalmente, è una buona pratica usare ErrorBoundary per incapsulare un componente che favorisce l'insorgenza di errori.

Con il metodo di ciclo di vita componentDidCatch() sei in grado di individuare errori durante la fase di rendering o in qualsiasi altro ciclo di vita dei componenti figli. Quindi, quando un errore si verifica in questa fase, arriva in superficie e viene colto dal componente ErrorBoundary.

Se stai usando un servizio di registrazione (che consiglio vivamente), è un ottimo posto da collegare.

La funzione statica getDerivedStateFromError() viene chiamata durante la fase di rendering e viene usata per aggiornare lo stato del componente ErrorBoundary. A seconda dello stato, puoi renderizzare condizionalmente un errore nell'interfaccia.

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    //registra l'errore con un ERS
    errorService.log({ error, errorInfo });
  }

  render() {
    if (this.state.hasError) {
      return <h1>Oops, something went wrong.</h1>;
    }
    return this.props.children; 
  }
}

Il grande svantaggio di questo approccio è che non gestisce gli errori in callback asincroni, nel rendering lato server e nei gestori di eventi perché sono al di fuori dei confini.

Usare try-catch per gestire gli errori oltre i confini

Questa tecnica è efficace per individuare errori che possono verificarsi all'interno di callback asincroni. Immagina di star recuperando i dati del profilo di un utente da una API a di volerli visualizzare all'interno di un componente Profile.

const UserProfile = ({ userId }) => {
	const [isLoading, setIsLoading] = useState(true)
	const [profileData, setProfileData] = useState({})
    
    useEffect(() => {
    	// Separate function to make of use of async
        const getUserDataAsync = async () => {
        	try {
            	// Recupera i dati dalla API
            	const userData = await axios.get(`/users/${userId}`)
                // Restituisci un errore se il dato dell'utente è falsy (sarà intercettato da catch)
                if (!userData) {
                	throw new Error("No user data found")
                }
                // Se il dato dell'utente è truthy aggiorna lo stato
                setProfileData(userData.profile)
            } catch(error) {
            	// Registra ogni errore nel servizio di logging
            	errorService.log({ error })
                // Aggiorna stato 
                setProfileData(null)
            } finally {
            	// Resetta il carimento dello stato in ogni caso
                setIsLoading(false)
            }
        }
        
        getUserDataAsync()
    }, [])
    
    if (isLoading) {
    	return <div>Loading ...</div>
    }
    
    if (!profileData) {
    	return <ErrorUI />
    }
    
    return (
    	<div>
        	...User Profile
        </div>
    )
}

Quando il componente viene montato, avvia una richiesta GET alla nostra API per ricevere i dati dell'utente per il corrispondente userId che otterremo dalle prop.

Usare try-catch ci aiuta a individuare qualsiasi errore che può capitare durante la chiamata dell'API. Ad esempio, potrebbe essere una risposta 404 o 500 dall'API.

Una volta che un errore viene individuato, siamo all'interno del blocco catch e riceviamo l'errore come parametro. Adesso siamo in grado di accedere al servizio di logging e aggiornare lo stato di conseguenza per mostrare un errore personalizzato nell'interfaccia utente.

Usare la libreria react-error-boundary library (raccomandazione personale)

In pratica, questa libreria fonde le due tecniche precedenti. Semplifica la gestione degli errori in React e rimedia alle limitazioni del componente ErrorBoundary che abbiamo visto.

import { ErrorBoundary } from 'react-error-boundary'

const ErrorComponent = ({ error, resetErrorBoundary }) => {
  
  return (
    <div role="alert">
      <p>Something went wrong:</p>
      <pre>{error.message}</pre>
    </div>
  )
}

const App = () => {
  const logError = (error, errorInfo) => {
  	errorService.log({ error, errorInfo })
  }
  

  return (
    <ErrorBoundary 
       FallbackComponent={ErrorComponent}
       onError={logError}
    >
       <MyErrorProneComponent />
    </ErrorBoundary>
  );
}

La libreria esporta un componente costituito dalle funzionalità ErrorBoundary che abbiamo già conosciuto e vi aggiunge alcune sfumature. Consente di passare un FallbackComponent come prop che dovrebbe essere renderizzato una volta individuato l'errore.

Espone anche una prop onError che fornisce una funzione callback quando si verifica un errore. È ottima da usare per registrare l'errore in un servizio di logging.

Ci sono alcune altre prop che sono piuttosto utili. Se vuoi saperne di più, dai pure un'occhiata alla documentazione.

Questa libreria fornisce un hook chiamato useErrorHandler() il cui scopo è individuare ogni errore oltre i confini come i gestori di eventi, nel codice asincrono e nel rendering lato server.

Registrare gli errori

Individuare e gestire efficacemente gli errori è una parte, registrarli in modo appropriato è un'altra. Una volta che hai impostato la gestione degli errori nella tua applicazione, devi registrarli in modo persistente.

Il modo usato più di frequente è il caro vecchio console.log. Potrebbe andare bene durante lo sviluppo, quando vuoi un log rapido. Ma una volta che l'applicazione viene distribuita in produzione, diventa inutile. Questo perché l'errore è visibile soltanto all'interno del browser dell'utente, il che è assolutamente inefficace.

Per registrare gli errori in produzione, in quanto sviluppatore, vuoi vedere gli errori in un posto dedicato per poterli sistemare.

Per questa ragione abbiamo bisogno di un servizio di logging, creato da noi o da terze parti.

Quando usiamo servizi di logging di terze parti, il mio suggerimento personale è senza dubbio Sentry. Quindi ti consiglio vivamente di dargli un'occhiata.

☝️ Mantenere una chiave unica in tutta la tua app

Mappando un array per renderizzare i suoi dati, devi sempre definire una proprietà chiave per ogni elemento. Una pratica comune che ho visto e usato in prima persona è utilizzare semplicemente l'indice di ogni elemento come chiave.

Usare la prop chiave è importante perché aiuta React a identificare l'esatto elemento che viene cambiato, aggiunto o rimosso. Immagina che lo stato del tuo componente cambi e che l'interfaccia utente debba essere ri-renderizzata con il nuovo stato. React ha bisogno di capire la differenza tra l'interfaccia precedente e quella nuova per poterla aggiornare.

"Quali elementi sono stati aggiunti/rimossi o sono cambiati?"

Per questo la chiave deve essere unica. Usare l'indice dell'elemento corrente assicura che sia unica solo in questa particolare funzione di mappatura.

Potrebbe avere questo aspetto, se avessimo deciso di mostrare i punteggi di una squadra di football di questa stagione:

const SeasonScores = ({ seasonScoresData }) => {
	
    return (
    	<>
        	<h3>Our scores in this season:<h3>
        	{seasonScoresData.map((score, index) => (
    			<div key={index}>
        			<p>{score.oponennt}</p>
        			<p>{score.value}</p>
        		</div>
    		))}
        </>
    )
}

Mentre è unica solamente all'interno di questa funzione di mappatura, ciò può portare a potenziali problemi. È molto comune avere più di una funzione di mappatura all'interno di un'applicazione React o anche in un componente.

Assumiamo di avere un'altra funzione di mappatura nel nostro componente per visualizzare l'elenco attuale:

const SeasonScores = ({ seasonScoresData, currentRoster }) => {
	
    return (
    	<>
        	<h3>Our scores in this season:<h3>
        	{seasonScoresData.map((score, index) => (
    			<div key={index}>
        			<p>{score.oponennt}</p>
        			<p>{score.value}</p>
        		</div>
    		))}
            </br>
			<h3>Our current roster:<h3>
        	{currentRoster.map((player, index) => (
            	<div key={index}>
                	<p>{player.name}</p>
                    <p>{player.position}</p>
                    <p>{player.jerseyNumber}</p>
                    <p>{player.totalGoals}</p>
                </div>
    		))}
        </>
    )
}

Ci ritroviamo nella situazione in cui usiamo molte chiavi due volte all'interno del nostro componente. Consideriamo di avere 14 elementi all'interno di seasonScoresData e 30 in currentRoaster. Abbiamo usato i numeri da 0 a 13 due volte come chiave. Ora non ci stiamo più attenendo allo scopo di avere chiavi uniche.

Ciò può portare a potenziali problemi perché React potrebbe ri-renderizzare soltanto un elemento e omettere l'altro. Oppure può portare a inefficienza nell'aggiornamento dell'albero dell'interfaccia. Dai un'occhiata ai post consigliati alla fine di questa sezione per avere degli esempi più dettagliati.

Per evitare questo comportamento indesiderato, assicurati di usare sempre chiavi uniche in tutta l'applicazione. Idealmente ogni elemento nell'array dovrebbe avere il proprio id univoco da utilizzare. Ma questo non è sempre il caso, quindi puoi usare una libreria esterna come uuidv4 per generare id univoci.

Con questo in mente e con l'assunzione che ogni elemento in entrambi gli array ha una proprietà id, il componente dovrebbe avere questo aspetto:

const SeasonScores = ({ seasonScoresData, currentRoster }) => {
	
    return (
    	<>
        	<h3>Our scores in this season:<h3>
        	{seasonScoresData.map((score, index) => (
    			<div key={score.id}>
        			<p>{score.oponennt}</p>
        			<p>{score.value}</p>
        		</div>
    		))}
            </br>
			<h3>Our current roster:<h3>
        	{currentRoster.map((player, index) => (
            	<div key={player.id}>
                	<p>{player.name}</p>
                    <p>{player.position}</p>
                    <p>{player.jerseyNumber}</p>
                    <p>{player.totalGoals}</p>
                </div>
    		))}
        </>
    )
}

Se vuoi andare più a fondo, puoi leggere questo post (risorsa in inglese) sull'argomento.

Suggerimenti per scrivere del codice React migliore – La ciliegina sulla torta

joanna-kosinska-_xN7UbcZ33I-unsplash

Vorrei paragonare questa guida al processo di costruzione di una casa. La prima parte, Imparare gli elementi fondamentali di React, corrisponde alle solide fondamenta su cui costruisci la tua applicazione. La seconda, Come realizzare dei componenti React puliti, prestanti e manutenibili, è per costruire i muri.

Questa sezioni è sostanzialmente il tetto che va a completare la casa. Ecco perché l'ho chiamata La ciliegina sulla torta. Contiene suggerimenti più dettagliati.

La maggior parte di queste pratiche sono più opzionali delle precedenti, ma possono fare la differenza se le utilizzi nel modo appropriato.

🪄 Implementare prima l'hook useReducer

Probabilmente uno degli hook usati più di frequente in React è useState. Nel tempo, ho creato e visto componenti con un sacco di stati differenti. Quindi è naturale che siano inondati da hook useState.

const CustomersMap = () => {
  const [isDataLoading, setIsDataLoading] = useState(false)
  const [customersData, setCustomersData] = useState([])
  const [hasError, setHasError] = useState(false)
  const [isHovered, setIsHovered] = useState(false)
  const [hasMapLoaded, setHasMapLoaded] = useState(false)
  const [mapData, setMapData] = useState({})
  const [formData, setFormData] = useState({})
  const [isBtnDisabled, setIsBtnDisabled] = useState(false)
  
  ...
  
  return ( ... )
}

Avere molti hook useState diversi è sempre un segno importante che la dimensione e quindi la complessità di un componente sta crescendo.

Se puoi creare dei sotto-componenti più piccoli in cui trasferire alcuni stati e del JSX, si tratta di un'ottima idea, così da ripulire gli hook useState e il JSX in un colpo.

Nell'esempio qui sopra, potremmo mettere gli ultimi due stati in un componente separato che gestisce tutti gli stati e il JSX che riguardano un modulo.

Ma ci sono situazioni in cui ciò non ha senso e devi mantenere tutti questi stati differenti in un solo componente. Per migliorare la leggibilità del componente, esiste l'hook useReducer.

La documentazione ufficiale di React riporta:

useReducer è solitamente da preferire a useState quando si ha una logica di stato complessa che coinvolge sotto-valori multipli o quando lo stato successivo dipende dal precedente. useReducer consente anche di ottimizzare le prestazioni per i componenti che attivano aggiornamenti profondi perché è possibile passare dei messaggi invece di callback.

Avendo questo in mente, il componente potrebbe avere questo aspetto usando useReducer:

// STATO INIZIALE
const initialState = {
  isDataLoading: false,
  customerData: [],
  hasError: false,
  isHovered: false,
  hasMapLoaded: false,
  mapData: {},
  formdata: {},
  isBtnDisabled: false
}

// REDUCER
const reducer = (state, action) => {
  switch (action.type) {
    case 'POPULATE_CUSTOMER_DATA':
      return {
        ...state,
        customerData: action.payload
      }
    case 'LOAD_MAP':
      return {
        ...state,
        hasMapLoaded: true
      }
    ...
    ...
    ...
    default: {
      return state
    }	
  }
}

// COMPONENTE
const CustomersMap = () => {
  const [state, dispatch] = useReducer(reducer, initialState)
  
  ...
  
  return ( ... )
}

Il componente stesso ha un aspetto più pulito e possiede anche alcuni vantaggi come puoi vedere nella documentazione. Se sei abituato a Redux, il concetto di reducer e di come viene costruito non ti è nuovo.

La mia regola personale è di implementare l'hook useReducer se un componente supera i quattro hook useState oppure se lo stato stesso è più complesso di un semplice booleano, ad esempio. Potrebbe trattarsi di un oggetto per un modulo con dei livelli più profondi all'interno.

🔌 Usare scorciatoie per prop booleane

Ci sono spesso situazioni in cui si passa una prop booleana a un componente. Ho visto molti sviluppatori fare così:

<RegistrationForm hasPadding={true} withError={true} />

Ma non è necessario farlo necessariamente così perché la ricorrenza di una prop stessa è truthy (se la prop viene passata) o falsy (se è mancante).

Ecco un approccio più chiaro:

<RegistrationForm hasPadding withError />

👎 Evitare le parentesi graffe per le prop stringhe

Un caso di utilizzo simile a quello visto nel suggerimento precedente è l'utilizzo di prop stringhe:

<Paragraph variant={"h5"} heading={"A new book"} />

Non occorre usare le parentesi graffe in questo caso perché puoi usare direttamente delle stringhe all'interno delle prop. Quando vuoi attribuire un className a un elemento JSX è più probabile che tu lo faccia direttamente come una stringa.

Quando vuoi usare un'espressione JavaScript diversa da una stringa, ti servono le parentesi graffe. Ad esempio, se vuoi usare un numero o un oggetto. Questo è vero anche per le stringhe template (non restarci invischiato come ho fatto molte volte, haha).

Con delle normali stringhe, come nell'esempio, diventerebbe così:

<Paragraph variant="h5" heading="A new book" />

🧹 Cancellare attributi non-html distribuendo le prop

Vediamo un esempio rapido:

const MainTitle = ({ isBold, children, ...restProps }) => {
	
  return (
    <h1 
      style={{ fontWeight: isBold ? 600 : 400 }}
      {...restProps}
    >
      {children}
    </h1>
  )
}

Abbiamo appena creato un componente che renderizza un tag h1, estratto delle prop e distribuito tutte le altre potenziali prop nel tag h1. Per ora tutto ok.

Adesso siamo in grado di usarlo in altri componenti e possiamo attivare manualmente se l'h1 dovrebbe essere in grassetto o no:

// CON TITOLO IN GRASSETTO
const IndexPage = () => {
	
  return (
    <>
      <MainTitle isBold>
        Welcome to our new site!
      </MainTitle>
      ...
    </>
  )
}
// SENZA TITOLO IN GRASSETTO
const AboutPage = () => {
	
  return (
    <>
      <MainTitle>
      	Some quick lines about us!
      </MainTitle>
      ...
    </>
  )
}

Finora, tutto funziona perfettamente senza errori o avvisi. La parte interessante inizia adesso, quando usiamo altre prop che sono direttamente distribuite al tag h1.

Quando usiamo degli attributi HTML validi come id o class, tutto funziona senza errori (ricorda --> "className" diventa "class"):

const IndexPage = () => {
	
  return (
    <>
      <MainTitle isBold id="index-main-title" className="align-left">
        Welcome to our new site!
      </MainTitle>
      ...
    </>
  )
}

Quindi tutte le prop qui sopra saranno aggiunte come attributo per l'h1 perché stiamo usando {...restProps}. Le prop che aggiungiamo e NON estraiamo saranno aggiunte al tag h1.

Questo è ottimo per molti casi ma può essere un problema allo stesso tempo:

// Page Component
const IndexPage = () => {
	
  return (
    <>
      <MainTitle isBold hasPadding>
        Welcome to our new site!
      </MainTitle>
      ...
    </>
  )
}

// MainTitle Component
const MainTitle = ({ isBold, children, ...restProps }) => {
	
  return (
    <h1 
      style={{ 
        fontWeight: isBold ? 600 : 400,
        padding: restProps.hasPadding ? 16 : 0
      }}
      {...restProps}
    >
      {children}
    </h1>
  )
}

Nel codice qui sopra abbiamo aggiunto una nuova prop chiamata hasPadding al componente MainTitle, che è opzionale. All'interno del componente non la estraiamo dalle prop e la chiamiamo tramite restProps.hasPadding.

Il codice funziona, ma aprendo il tuo browser riceverai un avviso che hasPadding non è un attributo HTML che stai tentando di applicare al tag h1. Questo per via di {...restProps} sul tag h1 e perché non stiamo estraendo hasPadding come isBold, ad esempio.

Per evitare tutto ciò, estrai sempre tutti gli attributi non-HTML dalle prop, così da assicurarti che ci siano solo attributi HTML validi in restProps che stai distribuendo a un elemento JSX.

Nel nostro esempio sarebbe così:

// Page Component
const IndexPage = () => {
	
  return (
    <>
      <MainTitle isBold hasPadding>
        Welcome to our new site!
      </MainTitle>
      ...
    </>
  )
}

// MainTitle Component
const MainTitle = ({ isBold, children, hasPadding, ...restProps }) => {
	
  return (
    <h1 
      style={{ 
        fontWeight: isBold ? 600 : 400,
        padding: hasPadding ? 16 : 0
      }}
      {...restProps}
    >
      {children}
    </h1>
  )
}

Molti di questi avvisi possono intasare la console nel browser senza che siano necessari, il che può essere sgradevole. Specialmente se stai facendo debugging.

Per ottenere più informazioni su questo argomento e altri modi per risolvere il problema, consulta questa sezione della documentazione.

🔥 Usare le estensioni snippet

In Visual Studio Code, ad esempio, sono disponibili alcune estensioni che migliorano la tua produttività. Tra queste ci sono le estensioni snippet.

Il loro grande vantaggio è che non devi scrivere tutto il codice boilerplate di nuovo. Immagina di star creando molti nuovi componenti a di dover digitare tutto ancora e ancora:

import React from 'react'

const GoogleMap = () => {

}

export default GoogleMap

Con questi snippet devi soltanto digitare rafce, ad esempio, premere tab ed ecco che hai lo stesso codice boilerplate. È davvero un salva tempo e accelera lo sviluppo.

Ma usali con attenzione! Non consiglio di usare snippet a tutti gli sviluppatori. Secondo me, i principianti non dovrebbero usare alcuno snippet e dovrebbero scrivere il boilerplate a mano. Quando lo fai, ottieni una memoria muscolare che esprime tutto ciò che hai imparato.

Se lo hai fatto così spesso da poterlo scrivere nel sonno e diventa noioso, allora è il momento giusto di usare gli snippet.

Ecco i miei consigli:

Bildschirmfoto-2022-02-01-um-14.55.02
Bildschirmfoto-2022-02-01-um-15.05.01
Bildschirmfoto-2022-02-01-um-15.06.59

❌ Scrivere un frammento quando non è necessario un div

Un componente React può renderizzare un solo tag HTML alla volta nella sua radice. Quindi volendo renderizzare due elementi adiacenti, otterrai il famoso errore Adjacent JSX elements must be wrapped in an enclosing tag (gli elementi JSX adiacenti devono essere racchiusi in un tag).

const InfoText = () => {
	
  // Darà un errore
  return (
    <h1>Welcome!</h1>
    <p>This our new page, we're glad you're are here!</p>
  )
}

Quindi, cosa puoi fare? Racchiudi semplicemente l'output in un frammento, che soddisfa React e non aggiunge nessun elemento extra nel browser.

const InfoText = () => {
	
  return (
  	<>
      <h1>Welcome!</h1>
      <p>This our new page, we're glad you're are here!</p>
    </>
  )
}

Naturalmente è possibile risolvere tutto ciò anche con un tag div. Ma usare un div dopo l'altro crea una cosa chiamata inferno dei div nel browser, in cui ci sono un sacco di tag div annidati senza senso.

Quindi ogni volta che devi usare un tag per racchiudere qualcosa in React ma non ti serve necessariamente un tag HTML, allora usa semplicemente un frammento.

👈 Integrare i tag a chiusura automatica quando i figli non sono necessari

Dalla mia esperienza posso dire che questo suggerimento è spesso trascurato, ma rende del codice molto più pulito con uno sforzo minimo.

In React, hai la possibilità di passare elementi figli a un componente, che poi sono disponibili al componente tramite la sua proprietà figlia. Questi componenti sono spesso chiamati componenti compositi.

In questo caso, devi utilizzare un tag di apertura e un tag di chiusura, naturalmente:

<NavigationBar>
  <p>Home</p>
  <p>About</p>
  <p>Projects</p>
  <p>Contact</p>
</NavigationBar>

Ma quando non sono necessari dei figli, non ha senso usare un tag di apertura e un tag di chiusura, vero?

<NavigationBar></NavigationBar>

Invece di fare così, consiglio di usare il componente come un elemento a chiusura automatica come il tag HTML input, che allo stesso modo non accetta figli.

<NavigationBar />

Ha un aspetto molto più pulito, vero?

✅ Seguire le convenzioni di nomenclatura più comuni

Il senso alla base delle convenzioni di nomenclatura è che è più semplice riconoscere il tipo di elemento con cui hai a che fare, oltre ad avere qualcosa nel tuo codice in comune con la comunità.

Da questo punto di vista, ci sono due principali convenzioni di nomenclatura che coinvolgono React e JavaScript che dovresti seguire:

Usare PascalCase in componenti, interfacce o alias di tipo

// Componente React
const LeftGridPanel = () => {
  ...
}

// Interfaccia Typescript
interface AdminUser {
  name: string;
  id: number;
  email: string;
}

// Alias di tipo Typescript
type TodoList = {
	todos: string[];
    id: number;
    name: string;
}

Usare camelCase per tipi di dati JavaScript come variabili, array, oggetti, funzioni e così via

const getLastDigit = () => { ... }

const userTypes = [ ... ]

Dare nomi ai componenti React in PascalCase è molto importante, perché se hai un linter configurato per React, ma i nomi dei tuoi componenti sono in camelCase e stai usando degli hook al loro interno, otterrai un messaggio di avvertimento tutte le volte che gli hook sono consentiti solo nei componenti. Questo perché il linter riconosce un componente React se è scritto in PascalCase oppure no.

Può essere spiacevole, ma si sistema rapidamente adottando le convenzioni stabilite.

🧨 Ripulire il codice per prevenire attacchi XSS

Magari ti sei trovato in una situazione in cui occorre usare la proprietà dangerouslySetInnerHTML su un elemento in React. Sostanzialmente è l'equivalente di React di innerHTML che potresti conoscere da JavaScript.

Quindi usandola puoi impostare direttamente l'HTML da React.

Consideriamo il seguente esempio, in cui vogliamo renderizzare una stringa HTML all'interno di un div. La stringa può venir fuori da un bell'editor di testo in cui c'è già dell'HTML formattato.

const Markup = () => {
  const htmlString = "<p>This is set via dangerouslySetInnerHTML</p>"
  
  return (
    <div dangerouslySetInnerHTML={{ __html: htmlString }} />
  )
}

Il termine dangerously è scelto intenzionalmente. Usare questa proprietà può permettere attacchi di cross-site-scripting (XSS). Quindi è obbligatorio che il codice venga prima ripulito.

Un'ottima libreria che può aiutarti in questo è dompurify.

Conclusione

Wow, è stato divertente, vero? Ho cercato di fare del mio meglio per far uscire tutto quello che avevo accatastato nella mia testa in passato. Quello che mi ha spinto a scrivere questa guida è condividere la mia esperienza con te in modo da farti evitare dei momenti difficili durante lo studio e lo sviluppo con React.

Naturalmente potrebbero esserci delle buone pratiche che consideri più importanti e che potrei aver tralasciato. Ottimo. Mi piacerebbe sentire da te quali sono per poterle aggiungere a questa guida.

Ricorda, si tratta sempre di adattarsi a ciò che utile. Quindi non dare tutto per scontato e pensa a cosa potrebbe esserti utile nella situazione in cui ti trovi. Poi puoi aggiungerlo al tuo elenco di buone pratiche.

Puoi seguire il mio percorso di sviluppo e ottenere molti altri utili consigli sulla vita da sviluppatore sul mio profilo Instagram. Sono sempre disponibili ad aiutare e sono felice di ricevere feedback. Sentiti libero di contattarmi.