Vai al contenuto principale
Questo documento descrive le migliori pratiche da seguire quando si lavora sul frontend.

Gestione dello Stato

React e Recoil gestiscono la gestione dello stato nella base di codice.

Usa useRecoilState per memorizzare lo stato

È buona pratica creare tanti atomi quanti servono per memorizzare il tuo stato.
È meglio usare atomi extra piuttosto che cercare di essere troppo concisi con l’iniezione di props.
export const myAtomState = atom({
  key: 'myAtomState',
  default: 'default value',
});

export const MyComponent = () => {
  const [myAtom, setMyAtom] = useRecoilState(myAtomState);

  return (
    <div>
      <input
        value={myAtom}
        onChange={(e) => setMyAtom(e.target.value)}
      />
    </div>
  );
}

Non utilizzare useRef per memorizzare lo stato

Evita di usare useRef per memorizzare lo stato. Se vuoi memorizzare lo stato, dovresti usare useState o useRecoilState. Consulta come gestire i re-render se senti che hai bisogno di useRef per evitare alcuni re-render.

Gestione dei Re-Render

I re-render possono essere difficili da gestire in React. Ecco alcune regole da seguire per evitare re-render non necessari. Tieni presente che puoi sempre evitare i re-render comprendendo la loro causa.

Lavora a livello radice

Evitare i re-render in nuove funzionalità è ora più semplice eliminandoli a livello radice. Il componente sidecar PageChangeEffect contiene un solo useEffect che detiene tutta la logica da eseguire su un cambio di pagina. In questo modo sai che c’è solo un luogo che può attivare un re-render.

Pensa sempre due volte prima di aggiungere useEffect nel tuo codice

I re-render sono spesso causati da useEffect non necessari. Dovresti pensare se hai bisogno di useEffect, o se puoi spostare la logica in una funzione gestore di eventi. Troverai generalmente facile spostare la logica in una funzione handleClick o handleChange. Puoi trovarli anche in librerie come Apollo: onCompleted, onError, ecc.

Usa un componente simile per estrarre la logica useEffect o di recupero dati

Se senti di dover aggiungere un useEffect nel tuo componente radice, dovresti considerare di estrarlo in un componente sidecar. Puoi applicare lo stesso per la logica di recupero dati, con i hook di Apollo.
// ❌ Sconsigliato, causerà re-render anche se i dati non cambiano,
//    perché useEffect deve essere ri-eseguito
export const PageComponent = () => {
  const [data, setData] = useRecoilState(dataState);
  const [someDependency] = useRecoilState(someDependencyState);

  useEffect(() => {
    if(someDependency !== data) {
      setData(someDependency);
    }
  }, [someDependency]);

  return <div>{data}</div>;
};

export const App = () => (
  <RecoilRoot>
    <PageComponent />
  </RecoilRoot>
);
// ✅ Consigliato, non causerà re-render se i dati non cambiano,
//   perché useEffect viene ri-eseguito in un altro componente fratello
export const PageComponent = () => {
  const [data, setData] = useRecoilState(dataState);

  return <div>{data}</div>;
};

export const PageData = () => {
  const [data, setData] = useRecoilState(dataState);
  const [someDependency] = useRecoilState(someDependencyState);

  useEffect(() => {
    if(someDependency !== data) {
      setData(someDependency);
    }
  }, [someDependency]);

  return <></>;
};

export const App = () => (
  <RecoilRoot>
    <PageData />
    <PageComponent />
  </RecoilRoot>
);

Usa stati di famiglia recoil e selettori di famiglia recoil

Gli stati e i selettori di famiglia Recoil sono un ottimo modo per evitare re-render. Sono utili quando hai bisogno di memorizzare una lista di elementi.

Non dovresti usare React.memo(MyComponent)

Evita di usare React.memo() perché non risolve la causa del re-render, ma interrompe invece la catena di re-render, il che può portare a comportamenti inaspettati e rendere il codice molto difficile da rifattorizzare.

Limita l’uso di useCallback o useMemo

Spesso non sono necessari e renderanno il codice più difficile da leggere e mantenere per un guadagno di prestazioni che è impercettibile.

Console.log

Le dichiarazioni console.log sono preziose durante lo sviluppo, offrendo informazioni in tempo reale sui valori delle variabili e sul flusso del codice. Tuttavia, lasciarli nel codice di produzione può causare diversi problemi:
  1. Prestazioni: Un logging eccessivo può influire sulle prestazioni di runtime, soprattutto nelle applicazioni lato client.
  2. Sicurezza: Registrare dati sensibili può esporre informazioni critiche a chiunque ispezioni la console del browser.
  3. Pulizia: Riempire la console di log può oscurare avvertimenti o errori importanti che sviluppatori o strumenti devono vedere.
  4. Professionalità: Gli utenti finali o i clienti che controllano la console e vedono una miriade di dichiarazioni di log potrebbero mettere in dubbio la qualità e la raffinatezza del codice.
Assicurati di rimuovere tutti i console.logs prima di distribuire il codice in produzione.

Denominazione

Denominazione delle Variabili

I nomi delle variabili dovrebbero descrivere precisamente lo scopo o la funzione della variabile.

Il problema con i nomi generici

I nomi generici nella programmazione non sono ideali perché mancano di specificità, portando all’ambiguità e riducendo la leggibilità del codice. Tali nomi non riescono a trasmettere lo scopo della variabile o della funzione, rendendo difficile per gli sviluppatori comprendere l’intento del codice senza un’indagine più approfondita. Questo può risultare in tempi di debug più lunghi, maggiore suscettibilità agli errori e difficoltà nella manutenzione e nella collaborazione. Nel frattempo, una denominazione descrittiva rende il codice autoesplicativo e più facile da navigare, migliorando la qualità del codice e la produttività dello sviluppatore.
// ❌ Sconsigliato, usa un nome generico che non comunica chiaramente
//    scopo o contenuto
const [value, setValue] = useState('');
// ✅ Consigliato, usa un nome descrittivo
const [email, setEmail] = useState('');

Alcune parole da evitare nei nomi delle variabili

  • fittizio

Gestori di Eventi

I nomi dei gestori degli eventi dovrebbero iniziare con handle, mentre on è un prefisso usato per nominare gli eventi nelle props dei componenti.
// ❌ Sconsigliato
const onEmailChange = (val: string) => {
  // ...
};
// ✅ Consigliato
const handleEmailChange = (val: string) => {
  // ...
};

Props Opzionali

Evita di passare il valore predefinito per una prop opzionale. ESEMPIO Guarda il componente EmailField definito di seguito:
type EmailFieldProps = {
  value: string;
  disabled?: boolean;
};

const EmailField = ({ value, disabled = false }: EmailFieldProps) => (
  <TextInput value={value} disabled={disabled} fullWidth />
);
Utilizzo
// ❌ Sconsigliato, passare lo stesso valore del valore predefinito non apporta alcun beneficio
const Form = () => <EmailField value="username@email.com" disabled={false} />;
// ✅ Consigliato, presume il valore predefinito
const Form = () => <EmailField value="username@email.com" />;

Componente come props

Cercate, per quanto possibile, di passare componenti non istanziati come props, così i figli possono decidere autonomamente quali props devono passare. L’esempio più comune per questo sono i componenti icona:
const SomeParentComponent = () => <MyComponent Icon={MyIcon} />;

// Nel componente MyComponent
const MyComponent = ({ MyIcon }: { MyIcon: IconComponent }) => {
  const theme = useTheme();

  return (
    <div>
      <MyIcon size={theme.icon.size.md}>
    </div>
  )
};
Per far sì che React capisca che il componente è un componente, è necessario usare PascalCase, per poi istanziarlo con <MyIcon>.

Prop Drilling: Mantienilo Minimal

Il prop drilling, nel contesto di React, si riferisce alla pratica di passare variabili di stato e i loro setter attraverso molti livelli di componenti, anche se i componenti intermedi non li usano. Anche se a volte è necessario, un eccessivo prop drilling può portare a:
  1. Diminuzione della leggibilità: Tracciare da dove proviene un prop o dove viene utilizzato può diventare complicato in una struttura di componenti profondamente nidificata.
  2. Sfide di manutenzione: Cambiamenti nella struttura dei props di un componente potrebbero richiedere aggiustamenti in diversi componenti, anche se non utilizzano direttamente il prop.
  3. Ridotta riutilizzabilità del componente: Un componente che riceve molti props solo per passarli diventa meno generico e più difficile da riutilizzare in contesti diversi.
Se ritieni di utilizzare eccessivo prop drilling, vedi migliori pratiche di gestione dello stato.

Importa

Quando importi, opta per gli alias designati anziché specificare percorsi completi o relativi. Gli alias
{
  alias: {
    "~": path.resolve(__dirname, "src"),
    "@": path.resolve(__dirname, "src/modules"),
    "@testing": path.resolve(__dirname, "src/testing"),
  },
}
Utilizzo
// ❌ Sconsigliato, specifica l'intero percorso relativo
import {
  CatalogDecorator
} from '../../../../../testing/decorators/CatalogDecorator';
import {
  ComponentDecorator
} from '../../../../../testing/decorators/ComponentDecorator';
// ✅ Consigliato, utilizza gli alias designati
import { CatalogDecorator } from '~/testing/decorators/CatalogDecorator';
import { ComponentDecorator } from 'twenty-ui/testing';

Validazione dello Schema

Zod è il validatore di schema per oggetti non tipizzati:
const validationSchema = z
  .object({
    exist: z.boolean(),
    email: z
      .string()
      .email('L\'indirizzo email deve essere valido'),
    password: z
      .string()
      .regex(PASSWORD_REGEX, 'La password deve contenere almeno 8 caratteri'),
  })
  .required();

type Form = z.infer<typeof validationSchema>;

Modifiche Incompatibili

Esegui sempre test manuali approfonditi prima di procedere per garantire che le modifiche non abbiano causato interruzioni altrove, dato che i test non sono ancora stati ampiamente integrati.