Un repository in formato markdown per esplorare React. Include appunti dettagliati su sintassi, componenti, gestione dello stato e pattern avanzati. Pensato come una guida per principianti ed esperti.
- memo
- useMemo
- useCallback
- Differenze tra useMemo e useCallback
- useEffect
- Quando ha senso utilizzare useEffect e quando invece no
- useLayoutEffect
- Differenze tra useEffect e useLayoutEffect
- useRef
- createRef
- Differenze tra createRef e useRef
- useId
- key
- Utilizzo di key su componenti singoli
- useState
- useReducer
- Differenze tra useState e useReducer
React.memo
è una funzione di ordine superiore fornita da React che serve a ottimizzare le prestazioni delle componenti funzionali. Il suo scopo principale è evitare rendering inutili di componenti che ricevono le stesse props in ingresso. In pratica, React.memo
memorizza il risultato del render di una componente e lo riutilizza se le props non sono cambiate tra un aggiornamento e l'altro.
Ecco come funziona nel dettaglio:
React.memo
avvolge una componente funzionale. Se le props della componente avvolta non cambiano, React salta il rendering della componente e riutilizza l'ultimo risultato renderizzato. Questo può migliorare notevolmente le prestazioni, specialmente in grandi alberi di componenti dove il re-rendering può essere costoso.
const MyComponent = React.memo(function MyComponent(props) {
// La tua logica di renderizzazione
return <div>{props.children}</div>;
});
React.memo
può accettare un secondo argomento, che è una funzione di confronto personalizzata. Questa funzione prende le vecchie props e le nuove props come argomenti e ritorna true
se il risultato del render sarà lo stesso, permettendo così a React di saltare il render. Se ritorna false
, React eseguirà il render della componente con le nuove props.
Ecco un esempio di una funzione di confronto personalizzata:
const areEqual = (prevProps, nextProps) => {
/*
Ritorna true se passando nextProps a renderizzare
la componente ritorna lo stesso risultato di passando prevProps,
altrimenti ritorna false
*/
return prevProps.value === nextProps.value;
};
const MyComponent = React.memo(function MyComponent(props) {
// La tua logica di renderizzazione
return <div>{props.value}</div>;
}, areEqual);
React.memo
è utile quando:
- Una componente renderizza spesso gli stessi dati.
- Una componente renderizza con un alto costo di calcolo.
- Una componente si trova in una posizione alta nell'albero dei componenti, e il suo re-rendering causa un effetto a cascata di re-renderings inutili nei suoi discendenti.
Mentre React.memo
può migliorare le prestazioni evitando render inutili, può anche portare a una maggiore complessità e uso di memoria, poiché le vecchie versioni delle props devono essere memorizzate per confrontarle con quelle nuove. Inoltre, non è sempre necessario: per esempio, per componenti che si aggiornano frequentemente con props diverse, l'uso di React.memo
potrebbe avere un impatto minimo o addirittura negativo sulle prestazioni.
In sintesi, React.memo
è uno strumento utile per ottimizzare componenti funzionali in React quando è adeguatamente giustificato da scenari di utilizzo che traggono beneficio dalla memorizzazione delle props e dalla riduzione dei re-render.
Il hook useMemo
di React è uno strumento che aiuta a ottimizzare le prestazioni delle funzioni componenti gestendo la memoizzazione dei calcoli. In sostanza, useMemo
ti permette di memorizzare un valore calcolato in modo che, durante i successivi render, se le dipendenze specificate non sono cambiate, il valore memorizzato possa essere riutilizzato senza dover ricalcolare di nuovo.
useMemo
prende due argomenti:
- Una funzione di creazione: Questa funzione genera il valore che vuoi memorizzare.
- Un array di dipendenze:
useMemo
ricalcolerà il valore memorizzato solo quando una delle dipendenze ha subito modifiche.
Ecco la sintassi di base di useMemo
:
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
In questo esempio, computeExpensiveValue
è una funzione che richiede tempo o risorse per essere calcolata. L'hook useMemo
si assicurerà che questa funzione venga eseguita solo quando i valori di a
o b
cambiano. Se a
e b
rimangono invariati tra i render, useMemo
fornirà il valore già calcolato senza eseguire di nuovo computeExpensiveValue
.
useMemo
è particolarmente utile quando:
- Devi gestire operazioni costose dal punto di vista delle risorse che non vuoi ripetere su ogni render.
- Hai componenti che ricevono prop complesse o calcolate che potrebbero non cambiare frequentemente.
Tuttavia, è importante notare che useMemo
non garantisce l'immunità ai re-render, ma mira a ridurre il costo dei calcoli durante i re-render.
Consideriamo un componente che esegue un calcolo intensivo dei dati che dipendono solo da una specifica prop:
const ExpensiveComponent = ({ items }) => {
const sortedItems = useMemo(() => {
console.log("Sorting items...");
return items.sort((a, b) => a.value - b.value);
}, [items]);
return (
<ul>
{sortedItems.map(item => (
<li key={item.id}>{item.value}</li>
))}
</ul>
);
};
In questo esempio, la lista items
viene ordinata. L'operazione di ordinamento viene eseguita solo quando items
cambia, il che può risparmiare una quantità significativa di risorse se items
rimane invariato tra render.
Mentre useMemo
può essere molto utile per migliorare le prestazioni, il suo uso improprio può anche introdurre complessità inutile. Ad esempio, memoizzare valori che sono poco costosi da calcolare o che cambiano molto frequentemente potrebbe non apportare benefici significativi e potrebbe addirittura aumentare il consumo di risorse a causa del lavoro aggiuntivo legato alla gestione della cache di memoizzazione.
In sintesi, useMemo
è uno strumento potente per ottimizzare il rendering delle componenti React, specialmente in scenari dove il calcolo delle prop è particolarmente oneroso o costoso da eseguire.
useCallback
è un altro hook fornito da React che è utilizzato per memorizzare funzioni, aiutando a prevenire la creazione di funzioni inutili nei componenti durante ogni render. Questo può essere particolarmente utile in scenari dove passare funzioni continue come prop può causare render inutili di componenti figli, specialmente quelli che implementano controlli di ottimizzazione delle prestazioni come React.memo
.
useCallback
è simile a useMemo
, ma è specificamente progettato per funzioni. Prende due argomenti:
- Funzione di callback: la funzione che vuoi memorizzare.
- Array di dipendenze: un array che determina quando la funzione deve essere ricreata. La funzione memorizzata verrà ricreata solo quando una delle dipendenze cambia.
Ecco la sintassi di base di useCallback
:
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
In questo esempio, doSomething
è una funzione che dipende dai valori di a
e b
. Con useCallback
, questa funzione viene ricreata solo se a
o b
cambiano. Se i valori rimangono invariati tra i render, la stessa funzione memorizzata viene riutilizzata.
useCallback
è utile quando:
- La funzione in questione viene passata come prop a componenti figli ottimizzati, come quelli avvolti in
React.memo
. - Vuoi evitare re-render inutili causati dalla ricreazione di funzioni nelle dipendenze di useEffect o altri hook simili.
Supponiamo di avere un componente che accetta una funzione di callback come prop:
const Button = React.memo(({ onClick, children }) => {
console.log('Button render');
return <button onClick={onClick}>{children}</button>;
});
const ParentComponent = () => {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount((prevCount) => prevCount + 1);
}, []); // Dipendenze vuote significa che la funzione non cambia mai
return (
<div>
<Button onClick={increment}>Increment</Button>
<p>Count: {count}</p>
</div>
);
};
In questo esempio, il componente Button
è avvolto in React.memo
per evitare render inutili. Tuttavia, senza useCallback
, ogni volta che ParentComponent
viene renderizzato, una nuova funzione increment
viene creata, causando il re-render di Button
. Con useCallback
, la funzione increment
viene creata una sola volta e riutilizzata nei render successivi, prevenendo re-render inutili di Button
.
Come con useMemo
, è importante non abusare di useCallback
. Memorizzare una funzione ha un costo in termini di risorse e complessità. Se la funzione non viene passata a componenti figli o utilizzata in modi che possono beneficiare della memorizzazione, l'uso di useCallback
potrebbe non apportare benefici significativi. Inoltre, se le dipendenze cambiano frequentemente, il vantaggio della memorizzazione potrebbe essere minimo.
In sintesi, useCallback
è un tool efficace per migliorare le prestazioni quando utilizzato in contesti appropriati, come il passaggio di callback a componenti figli che sono sensibili a cambiamenti di prop.
useMemo
e useCallback
sono entrambi hook in React che aiutano a ottimizzare le prestazioni delle applicazioni evitando calcoli o azioni inutili nei re-render. Sebbene abbiano scopi simili nel ridurre i re-render superflui, essi sono usati in contesti leggermente diversi e gestiscono tipi di valori differenti. Ecco una panoramica dettagliata delle loro differenze:
-
Scopo:
useMemo
è utilizzato per memorizzare valori calcolati. L'obiettivo è evitare calcoli costosi che non hanno bisogno di essere eseguiti su ogni render se le dipendenze non cambiano. -
Tipo di Valori Gestiti: Memorizza qualsiasi valore calcolato, come numeri, stringhe, oggetti, array, componenti JSX, ecc.
-
Utilizzo: È particolarmente utile quando hai operazioni che richiedono un alto carico computazionale o risorse, come il filtraggio di dati, operazioni su array, manipolazione di oggetti, ecc.
-
Sintassi: La sintassi di
useMemo
è la seguente:const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
Dove
computeExpensiveValue
è una funzione che calcola un valore e[a, b]
sono le dipendenze.
-
Scopo:
useCallback
è utilizzato per memorizzare funzioni. Serve a prevenire la creazione di nuove funzioni su ogni render, che può essere costosa in termini di prestazioni se queste funzioni sono passate a componenti figli che dipendono da uguali riferimenti di prop per evitare render inutili. -
Tipo di Valori Gestiti: Memorizza funzioni.
-
Utilizzo: È utile quando passi funzioni come props a componenti ottimizzati (per esempio, componenti avvolti in
React.memo
) o quando una funzione è usata come dipendenza in altri hook comeuseEffect
,useMemo
, ecc., e vuoi assicurarti che la funzione non cambi ad ogni render se le sue dipendenze sono invariate. -
Sintassi: La sintassi di
useCallback
è la seguente:const memoizedCallback = useCallback(() => { doSomething(a, b); }, [a, b]);
Qui,
doSomething
è la funzione che vuoi memorizzare e[a, b]
sono le dipendenze.
Entrambi gli hook useMemo
e useCallback
ricevono un array di dipendenze e ricreano il valore memorizzato o la funzione solo quando una di queste dipendenze cambia. La differenza chiave è nel tipo di valore che gestiscono e memorizzano.
- useMemo: Memorizza il risultato di una funzione e lo ritorna. È focalizzato sul risultato di una computazione.
- useCallback: Memorizza la funzione stessa, assicurandosi che l'identità della funzione rimanga consistente tra i render. È focalizzato sull'identità della funzione.
In conclusione, sebbene entrambi siano usati per ottimizzare le prestazioni evitando lavoro inutile nei re-render, la scelta tra useMemo
e useCallback
dipende da cosa stai cercando di ottimizzare: calcoli di valori o consistenza di riferimento di funzioni.
useEffect
è un hook fondamentale in React che permette di eseguire effetti collaterali in componenti funzionali. Questo hook è essenziale per eseguire operazioni che richiedono sincronizzazione con il ciclo di vita del componente, come richieste API, sottoscrizioni, manipolazioni del DOM, timer, e altro ancora, che tradizionalmente erano gestite nei metodi di ciclo di vita in componenti di classe come componentDidMount
, componentDidUpdate
, e componentWillUnmount
.
useEffect
prende due parametri:
- Funzione di effetto: La funzione che contiene il codice dell'effetto collaterale che vuoi eseguire.
- Array di dipendenze: Un array opzionale che specifica quando l'effetto deve essere riattivato (eseguito di nuovo). L'effetto viene eseguito dopo ogni render in cui le dipendenze cambiano. Se l'array è vuoto (
[]
), l'effetto si attiva solo una volta dopo il primo render, simulando il comportamento dicomponentDidMount
. Se non si fornisce l'array di dipendenze, l'effetto verrà eseguito dopo ogni render.
useEffect(() => {
// Codice per l'effetto collaterale
return () => {
// Codice per la pulizia dell'effetto, opzionale
};
}, [dependencies]); // Dipendenze che, se cambiano, riattivano l'effetto
useEffect(() => {
console.log("Componente montato");
return () => {
console.log("Componente smontato");
};
}, []);
In questo esempio, il codice all'interno di useEffect
verrà eseguito una sola volta dopo il montaggio del componente, e la funzione di pulizia sarà chiamata quando il componente sarà smontato, simile a componentDidMount
e componentWillUnmount
.
useEffect(() => {
console.log("Valore aggiornato:", value);
return () => {
console.log("Pulizia per", value);
};
}, [value]); // Esegue l'effetto solo se `value` cambia
Questo useEffect
monitora cambiamenti di value
. Ogni volta che value
cambia, il codice viene eseguito, e la funzione di pulizia viene eseguita prima del prossimo effetto o quando il componente viene smontato.
La funzione di pulizia è particolarmente utile per rimuovere sottoscrizioni, cancellare timer o eseguire qualsiasi altra pulizia necessaria per evitare perdite di memoria e comportamenti imprevisti quando il componente viene smontato o prima che l'effetto venga riattivato.
- Performance: L'uso eccessivo di
useEffect
con dipendenze che cambiano frequentemente può portare a prestazioni ridotte, poiché l'effetto viene eseguito troppo spesso. - Ordine di esecuzione: Gli effetti sono garantiti per essere chiamati dopo il DOM è stato aggiornato, quindi non vedrai blocchi nel rendering UI durante l'esecuzione di un effetto.
- Bug Comuni: Uno dei problemi più comuni nell'uso di
useEffect
è includere dipendenze errate o dimenticarle, portando a effetti che non si attivano come previsto o che si attivano troppo spesso.
useEffect
è uno strumento potente e versatile in React, permettendo di integrare logica imperativa in modo sicuro e coordinato nel flusso di render dichiarativo di React.
L'uso del hook useEffect
in React è essenziale per gestire gli effetti collaterali nei componenti funzionali. Tuttavia, è importante sapere quando è appropriato utilizzarlo e quando potrebbe non esserlo. Di seguito, esamineremo le situazioni in cui useEffect
è particolarmente utile e quelle in cui potrebbe non essere la scelta migliore.
-
Manipolazioni DOM esterne a React: Se hai bisogno di integrare con librerie terze, manipolare il DOM in modi che React non gestisce direttamente, o se devi usare API del DOM che React non copre.
-
Sottoscrizioni e Event Listener: Per registrare e deregistrare event listener o per gestire sottoscrizioni a fonti di dati esterne, come WebSocket o servizi di notifica.
-
Richieste di rete:
useEffect
è utile per eseguire richieste HTTP (ad esempio, con fetch o axios) dopo il montaggio di un componente, garantendo che il componente sia pronto per visualizzare i dati ricevuti senza bloccare il rendering iniziale. -
Timer e Interval: Per impostare timer, come
setTimeout
osetInterval
,useEffect
fornisce un modo naturale di garantire che questi timer siano cancellati correttamente per prevenire effetti indesiderati quando il componente viene smontato. -
Effetti con pulizia: Quando hai bisogno di eseguire una pulizia relativa a effetti collaterali (come rimuovere event listener o sottoscrizioni) per evitare perdite di memoria.
-
Calcoli sincroni direttamente legati al render: Se un calcolo può essere eseguito sincronamente durante il render senza causare effetti collaterali o operazioni asincrone, non è necessario usare
useEffect
. Utilizzare invece il normale flusso di rendering di React ouseMemo
per ottimizzare calcoli costosi basati su specifiche dipendenze. -
Stato che non causa effetti collaterali: Se stai aggiornando lo stato che non è direttamente collegato a effetti collaterali, usa
useState
direttamente nel flusso di rendering.useEffect
potrebbe introdurre ritardi non necessari o complessità. -
Renderizzazioni condizionali e logica: Non utilizzare
useEffect
per decidere se renderizzare o meno qualcosa basato su condizioni. Questa logica dovrebbe essere gestita all'interno del corpo principale della funzione del componente o tramite l'uso di hook comeuseMemo
ouseState
.
- Minimizzare le dipendenze: Includi nelle dipendenze solo i valori che, se cambiati, richiedono effettivamente la ri-esecuzione dell'effetto. Un uso errato delle dipendenze può portare a cicli infiniti di render o a comportamenti inattesi.
- Evitare effetti eccessivi: Non mettere troppi effetti collaterali in un unico
useEffect
. Se possibile, suddividili in piùuseEffect
per chiarire le intenzioni e migliorare la riusabilità. - Attenzione ai cicli infiniti: Assicurati che gli effetti che aggiornano lo stato o le prop non riattivino sé stessi senza condizioni chiare di arresto.
In sintesi, useEffect
è potente per gestire operazioni che vanno oltre il calcolo immediato e sincrono dei dati, specialmente quelle che interagiscono con il mondo esterno o che necessitano di pulizie post-uso. Utilizzalo saggiamente per mantenere i componenti puliti, efficaci e facili da mantenere.
useLayoutEffect
è un hook di React molto simile a useEffect
, ma con una differenza chiave nel timing della sua esecuzione. Entrambi permettono di eseguire effetti collaterali nei componenti funzionali, ma useLayoutEffect
si esegue in un momento diverso del ciclo di vita del componente rispetto a useEffect
.
-
Timing di esecuzione:
useEffect
viene eseguito dopo che il DOM è stato aggiornato e la paint ha avuto luogo, il che significa che non blocca la visualizzazione visuale dell'aggiornamento dell'UI. In contrasto,useLayoutEffect
viene eseguito immediatamente dopo che sono state calcolate le modifiche al DOM ma prima che la paint avvenga. Questo significa cheuseLayoutEffect
ha la capacità di leggere e modificare il DOM prima che il browser abbia la possibilità di dipingere. -
Uso: Poiché
useLayoutEffect
viene eseguito prima della paint, è adatto per aggiornamenti che hanno bisogno di essere sincronizzati con il layout o altre operazioni visive per evitare effetti visivi indesiderati come il flickering.
useLayoutEffect
è particolarmente utile in scenari specifici dove le modifiche al DOM devono essere misurate o dove gli aggiornamenti devono essere visivamente sincronizzati per evitare effetti indesiderati. Alcuni esempi includono:
-
Misurazioni del DOM: Se hai bisogno di leggere le dimensioni o la posizione di un elemento nel DOM e basare su di esso degli aggiornamenti. L'uso di
useEffect
potrebbe causare un breve sfarfallio perché il DOM potrebbe essere visualizzato prima che l'effetto venga eseguito. -
Aggiornamenti visivi critici: Per eseguire modifiche che hanno un impatto visivo critico e che devono essere completate prima che la pagina venga visualizzata, per evitare artefatti visivi come sfarfallii o salti di layout.
Supponiamo di dover misurare l'altezza di un elemento subito dopo il suo rendering per decidere la posizione di un altro elemento. Qui è dove useLayoutEffect
sarebbe appropriato:
function MyComponent() {
const [height, setHeight] = useState(0);
const ref = useRef(null);
useLayoutEffect(() => {
if (ref.current) {
setHeight(ref.current.clientHeight);
}
}, [ref.current]); // Dipendenze che controllano l'esecuzione dell'effetto
return (
<>
<div ref={ref}>Misura questo elemento</div>
<div style={{ marginTop: height }}>Questo elemento si sposta in base all'elemento sopra</div>
</>
);
}
In questo esempio, useLayoutEffect
viene utilizzato per assicurarsi che l'altezza dell'elemento venga misurata e utilizzata per aggiornare lo stile di un altro elemento prima che il browser abbia la possibilità di dipingere, evitando così qualsiasi possibile flickering o salto visivo.
Mentre useLayoutEffect
è molto potente per gestire gli aggiornamenti sincronizzati con il layout, dovrebbe essere utilizzato con cautela perché può bloccare le visualizzazioni e influenzare le prestazioni se usato in eccesso. In molti casi, useEffect
è più che adeguato e dovrebbe essere la scelta predefinita a meno che non ci sia una ragione specifica per necessitare di un controllo più stretto del timing del ciclo di vita dell'aggiornamento del DOM.
useEffect
e useLayoutEffect
sono entrambi hook in React utilizzati per gestire effetti collaterali nei componenti funzionali. Sebbene siano simili nelle loro funzionalità e sintassi, differiscono principalmente nel momento in cui i loro callback vengono eseguiti durante il ciclo di vita del componente. Questa differenza di timing può avere impatti significativi sull'esperienza dell'utente e sulla performance dell'applicazione.
-
useEffect
: Viene eseguito dopo che il DOM è stato aggiornato e la fase di paint è stata completata. Questo comporta che qualsiasi operazione eseguita dentrouseEffect
non bloccherà la visualizzazione visiva dell'aggiornamento dell'UI, riducendo il rischio di causare rallentamenti o sfarfallii visivi. Gli effetti qui sono pianificati per l'esecuzione "dopo che tutto è fatto", quindi sono asincroni rispetto al processo di rendering. -
useLayoutEffect
: Viene eseguito subito dopo che le modifiche al DOM sono state calcolate ma prima che il rendering (paint) venga effettuato. Ciò significa che è possibile leggere e modificare il DOM e le proprietà di layout senza che il browser abbia tempo di dipingere, evitando così sfarfallii visivi. Tuttavia, poichéuseLayoutEffect
viene eseguito in una fase critica, l'uso improprio o operazioni pesanti all'interno di questo hook possono portare a una percezione di lentezza dell'applicazione.
-
useEffect
:- È adatto per la maggior parte degli effetti collaterali, specialmente quelli non legati direttamente al layout o al rendering, come richieste di rete, sottoscrizioni a servizi esterni, impostazioni di timer, e tracciamento di analytics.
useEffect
è la scelta di default per gli effetti collaterali perché non interferisce con il processo di paint del browser.
-
useLayoutEffect
:- Deve essere usato quando è necessario fare modifiche al DOM o eseguire calcoli basati sulle dimensioni o le posizioni del DOM prima che il browser inizi a dipingere. Questo previene potenziali sfarfallii o salti visivi che possono accadere quando le modifiche vengono visualizzate troppo tardi.
- Esempi tipici includono l'aggiustamento delle dimensioni di un elemento, la lettura delle proprietà di layout per decidere successive azioni o aggiornamenti, e la gestione di animazioni o transizioni imperative.
Supponiamo di dover misurare l'altezza di un div per poi posizionare un altro div direttamente sotto di esso senza che l'utente percepisca alcun sfarfallio tra il calcolo e l'applicazione del stile:
function MyComponent() {
const [topDivHeight, setTopDivHeight] = useState(0);
const topDivRef = useRef(null);
useLayoutEffect(() => {
const height = topDivRef.current.getBoundingClientRect().height;
setTopDivHeight(height);
}, []); // Eseguiamo questo con useLayoutEffect per evitare sfarfallii
return (
<>
<div ref={topDivRef}>Top Div</div>
<div style={{ marginTop: topDivHeight }}>Bottom Div</div>
</>
);
}
In questo esempio, useLayoutEffect
è utilizzato per garantire che il calcolo dell'altezza e l'applicazione del margine avvengano in modo sincronizzato con il ciclo di paint del browser, prevenendo così possibili discontinuità visive.
Scegliere tra useEffect
e useLayoutEffect
dipende fortemente dal tipo di operazione che si intende eseguire. Se l'operazione influisce sulla visualizzazione o sulle dimensioni dei componenti, useLayoutEffect
è generalmente la scelta migliore. Per tutti gli altri tipi di effetti collaterali, useEffect
è preferibile per evitare potenziali impatti negativi sulla performance.
Il hook useRef
è uno degli strumenti forniti da React per gestire le referenze all'interno dei componenti funzionali. Questo hook è particolarmente utile in diverse situazioni, come l'accesso diretto agli elementi del DOM, la conservazione di un valore mutable che non causa il re-render del componente quando cambia, e la persistenza di valori tra i render senza causare aggiornamenti aggiuntivi.
useRef
restituisce un oggetto ref mutabile il cui .current
proprietà è inizializzata al valore passato come argomento (initialValue
). L'oggetto restituito rimarrà lo stesso tra i render del componente:
const refContainer = useRef(initialValue);
- Persistenza: L'oggetto ref persiste durante la vita del componente senza cambiare, anche tra diversi render.
- Non Triggera Re-render: A differenza di
useState
, cambiare il valore di.current
di un ref non causa il re-render del componente. - Accesso al DOM: Una delle funzioni più comuni di
useRef
è accedere a un elemento del DOM per gestire il focus, selezionare testo, o per qualsiasi altra manipolazione diretta.
useRef
può essere utilizzato per collegare un elemento del DOM a una variabile ref, permettendo l'accesso diretto a quell'elemento:
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// Esplicitamente focus l'input usando la raw DOM API
if(inputEl.current) {
inputEl.current.focus();
}
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
In questo esempio, inputEl
è usato per fare riferimento a un campo di input del DOM. Il focus può essere impostato su questo campo tramite un pulsante.
useRef
è utile anche per conservare qualsiasi valore che vuoi persistere attraverso i render senza causare aggiornamenti quando cambia:
function TimerComponent() {
const count = useRef(0);
useEffect(() => {
setInterval(() => {
count.current = count.current + 1;
console.log(`Timer: ${count.current}`);
}, 1000);
}, []); // L'array di dipendenze vuoto assicura che l'effetto viene eseguito una sola volta
return <div>Check the console for the timer count.</div>;
}
In questo caso, count
è usato per tenere traccia di quante volte è passato un intervallo di tempo. Nota che le modifiche a count.current
non causano il re-render del componente.
- Usi appropriati:
useRef
è utile quando hai bisogno di una "scatola" persistente che può contenere un valore mutable che non deve influenzare il processo di rendering quando cambia. - Usi non appropriati: Non utilizzare
useRef
semplicemente per memorizzare uno stato se quel valore deve causare un aggiornamento dell'interfaccia utente quando cambia. In quel caso,useState
ouseReducer
sono più appropriati.
In conclusione, useRef
è uno strumento essenziale per gestire sia l'accesso ai nodi del DOM in un modo che non è possibile con i componenti funzionali tradizionali, sia per conservare dati mutable che non devono causare re-render quando cambiano.
createRef
è una funzione di React utilizzata per creare riferimenti, comunemente chiamati refs, che permettono di accedere direttamente agli elementi del DOM o ai componenti React in un'applicazione. Questa funzione è particolarmente utile nei componenti basati su classi, dove useRef
non è disponibile (poiché useRef
è specifico per i componenti funzionali).
Quando usi createRef
, ottieni un oggetto con una proprietà current
. Questa proprietà current
è dove viene conservato il riferimento all'elemento del DOM o al componente React dopo che il componente è stato montato.
Ecco la sintassi di base per creare e utilizzare un ref con createRef
in un componente di classe:
import React, { Component, createRef } from 'react';
class MyComponent extends Component {
constructor(props) {
super(props);
// Crea un ref
this.myRef = createRef();
}
componentDidMount() {
// Puoi accedere all'elemento del DOM o al componente React tramite this.myRef.current
if(this.myRef.current) {
console.log(this.myRef.current); // Accede al nodo DOM
}
}
render() {
// Assegna il ref a un elemento DOM o componente React
return <div ref={this.myRef}>Hello, World!</div>;
}
}
In questo esempio, createRef
crea un ref, this.myRef
, che viene poi associato a un elemento div
nel metodo render
. Dopo il montaggio del componente, this.myRef.current
conterrà una referenza diretta a quel div
, permettendo manipolazioni dirette o accessi a proprietà specifiche dell'elemento.
Mentre createRef
e useRef
hanno scopi simili, ci sono alcune differenze chiave:
-
Scopo di utilizzo:
createRef
è adatto per l'uso nei componenti di classe, mentreuseRef
è destinato ai componenti funzionali. -
Comportamento di persistenza:
createRef
crea sempre un nuovo ref ad ogni render, il che significa che ogni volta che il componente si aggiorna, viene fornito un nuovo oggetto ref. D'altra parte,useRef
fornisce un oggetto ref persistente che dura per tutta la vita del componente, mantenendo la sua consistenza tra i render.
createRef
è utile quando:
- Hai bisogno di accedere o manipolare direttamente un elemento del DOM in un componente di classe, ad esempio per impostare il focus, selezionare testo, o per qualsiasi altra interazione diretta.
- Devi interagire con metodi di un componente figlio che non sono esposti tramite props.
- Uso con cautela: L'uso eccessivo dei refs può portare a un codice che viola i principi dichiarativi e reattivi di React. È generalmente consigliato utilizzare i refs solo quando strettamente necessario, ad esempio quando non è possibile utilizzare tecniche di data flow standard di React per risolvere un problema.
- Componenti di classe vs funzionali: Con l'adozione crescente dei componenti funzionali e dei hook in React, l'uso di
createRef
è diventato meno comune. Tuttavia, rimane una parte essenziale della toolbox per chi lavora con i componenti di classe.
In sintesi, createRef
è uno strumento importante per la gestione diretta degli elementi del DOM e dei componenti React in scenari specifici, soprattutto all'interno dei componenti di classe.
useRef
e createRef
sono due funzioni fornite da React per creare referenze (refs) agli elementi del DOM o ai componenti React. Sebbene entrambi servano a scopi simili, differiscono significativamente nella loro applicazione, comportamento e contesti d'uso. Ecco un dettaglio più approfondito sulle differenze tra questi due approcci:
-
useRef
:- Contesto: Utilizzato nei componenti funzionali.
- Persistenza:
useRef
fornisce un oggetto ref la cui proprietà.current
è persistente attraverso i render del componente. Questo significa che il valore di.current
rimane invariato tra i render, a meno che non venga esplicitamente modificato.
-
createRef
:- Contesto: Utilizzato nei componenti basati su classi.
- Persistenza:
createRef
crea un nuovo oggetto ref ogni volta che il componente viene renderizzato. Ciò significa che otterrai un nuovo oggetto ref ad ogni render, che può essere problematico se non gestito correttamente, in quanto le referenze precedenti vengono perse.
-
useRef
:import React, { useRef } from 'react'; function MyFunctionalComponent() { const myRef = useRef(null); // myRef.current può essere utilizzato per accedere agli elementi DOM o componenti React return <div ref={myRef}>Ciao, Mondo!</div>; }
In questo esempio,
useRef
viene utilizzato in un componente funzionale.myRef.current
rimarrà consistente attraverso i render successivi. -
createRef
:import React, { Component, createRef } from 'react'; class MyClassComponent extends Component { constructor(props) { super(props); this.myRef = createRef(); } render() { // Ogni volta che il componente renderizza, myRef si attacca al nuovo nodo DOM return <div ref={this.myRef}>Ciao, Mondo!</div>; } }
Qui,
createRef
viene utilizzato in un componente basato su classe. SeMyClassComponent
viene renderizzato più volte,this.myRef
sarà inizializzato di nuovo ad ogni render.
-
Memorizzazione e Consistenza: A causa della sua natura persistente,
useRef
è particolarmente utile per mantenere qualsiasi valore (non solo elementi DOM) attraverso i render senza forzare il re-render del componente. Per esempio, può essere usato per mantenere una variabile di istanza come un contatore o una flag di stato. -
Ciclo di Vita e Re-render: L'uso di
createRef
in un componente di classe può essere problematico in scenari di render frequenti, poiché la reinizializzazione della ref può causare la perdita delle interazioni precedenti con il DOM o i componenti. Questo è meno di un problema conuseRef
, che mantiene la stessa ref attraverso aggiornamenti del componente.
La scelta tra useRef
e createRef
dipende dal tipo di componente che stai utilizzando (funzionale vs. classe) e dalla necessità di persistenza delle referenze tra i render. useRef
offre una gestione più semplice e consistente delle refs nei componenti funzionali, mentre createRef
è adatto per l'uso nei componenti di classe ma richiede attenzione nella gestione dei cicli di vita del componente per evitare problemi legati alla reinizializzazione delle refs.
useId
è un hook introdotto in React 18 che fornisce un modo per generare identificatori unici e stabili per i componenti, che possono essere utili in vari casi d'uso, specialmente quando si tratta di accessibilità, gestione del form, o quando hai bisogno di collegare elementi HTML tra loro, come label e input.
Il hook useId
è progettato per generare un ID unico e stabile che non cambia tra i render. Questo è particolarmente utile in React per vari motivi:
- Accessibilità (a11y): Gli ID unici sono essenziali per associare le etichette (
<label>
) agli input (<input>
) in moduli HTML, che è una pratica importante per l'accessibilità. - Gestione Form: Spesso, in form complessi o dinamici, è necessario mantenere un riferimento stabile agli elementi per gestire il focus, la validazione, o altre logiche di interazione.
- SSR (Server-Side Rendering):
useId
è progettato per funzionare con il server-side rendering, garantendo che gli ID generati lato server corrispondano a quelli generati lato client durante il reidratazione, prevenendo così discrepanze che potrebbero portare a bug o incoerenze nel rendering.
Ecco un esempio pratico di come useId
può essere utilizzato per associare una label a un input in un componente funzionale React:
import { useId } from 'react';
function MyInputComponent() {
const id = useId();
return (
<div>
<label htmlFor={id}>Nome:</label>
<input id={id} type="text" />
</div>
);
}
In questo esempio, useId
genera un ID che viene utilizzato sia per l'attributo htmlFor
della <label>
sia per l'attributo id
dell'<input>
. Questo assicura che il label sia correttamente associato all'input, migliorando l'accessibilità e facilitando l'interazione dell'utente con il form.
- Unicità e Stabilità: Gli ID generati sono unici e stabili durante i render, il che aiuta a prevenire problemi comuni in applicazioni dinamiche dove gli ID potrebbero altrimenti collidere o cambiare inaspettatamente.
- Performance: L'uso di
useId
è ottimizzato per essere leggero e performante, non influenzando negativamente le prestazioni dell'applicazione. - Compatibilità:
useId
è stato introdotto in React 18, quindi l'utilizzo di questo hook richiede che il tuo progetto sia aggiornato a questa versione o versioni successive di React.
In conclusione, useId
è un'aggiunta significativa agli hook di React che risolve problemi pratici di gestione degli ID in applicazioni web, particolarmente in contesti di accessibilità e form. Utilizzarlo consente di semplificare la gestione degli ID senza dover ricorrere a soluzioni meno stabili o più complesse.
Nella programmazione con React, la prop key
è un concetto fondamentale che aiuta a gestire l'identificazione e il riordinamento degli elementi in liste dinamiche o altri casi in cui i componenti possono essere aggiunti, rimossi o riordinati. La prop key
è particolarmente importante nella gestione delle prestazioni e nella corretta applicazione delle modifiche al DOM virtuale, il quale è poi sincronizzato con il DOM reale.
La key
è un attributo speciale che dovresti includere sugli elementi generati durante il rendering di liste o array. È un identificatore unico che aiuta React a determinare quali elementi sono cambiati, aggiunti o rimossi tra i render, facilitando così un aggiornamento efficiente del DOM.
Quando renderizzi una lista di elementi senza specificare una key
per ciascuno di essi, React usa l'indice dell'array come key
di default. Tuttavia, l'uso dell'indice come key
può portare a problemi di prestazione e a bug sottili se la lista può cambiare, specialmente se gli elementi possono essere riordinati o se possono essere aggiunti o rimossi degli elementi.
Utilizzando key
appropriate, React può:
- Mantenere lo stato locale dei componenti: Se un elemento della lista cambia la sua posizione, React può mantenere lo stato associato a quel componente (come lo stato del componente o lo stato del focus) perché riconosce l'elemento attraverso la sua
key
. - Effettuare aggiornamenti minimi al DOM: Se parti di una lista cambiano, React ha bisogno solo di modificare il DOM per quegli elementi specifici che sono cambiati, invece di riscrivere l'intera lista nel DOM, migliorando significativamente le prestazioni.
- Unicità: Le
key
devono essere uniche tra gli elementi fratelli. Non è necessario che siano globalmente uniche nel componente. - Stabilità: Le
key
dovrebbero essere stabili, consistenti e prevedibili. Devono rimanere le stesse tra i render per ogni elemento. - Non Usare Indici come
key
se la Lista Può Cambiare: Se la lista è soggetta a modifiche (come l'aggiunta, la rimozione o il riordinamento di elementi), usare l'indice comekey
è una pratica sconsigliata. Invece, usa un ID unico o un altro identificatore che non cambia nel tempo per quell'elemento.
Ecco un esempio di come si potrebbero usare le key
in un componente che renderizza una lista:
function TodoList({ todos }) {
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}
In questo esempio, ogni todo ha un id
che viene usato come key
. Questo consente a React di gestire efficacemente l'aggiornamento della lista quando i todo vengono aggiunti, rimossi o modificati.
L'uso appropriato delle key
è vitale per garantire prestazioni ottimali e comportamento corretto nell'aggiornamento delle liste e di altri set di componenti dinamici in React. Assicurandosi che ogni elemento abbia una key
unica e stabile, si può evitare la riscrittura inutile di elementi e mantenere uno stato coerente all'interno dei componenti tra i render.
L'attributo key
in React è comunemente associato all'uso in liste e array per aiutare React a identificare in modo univoco e gestire gli elementi dell'elenco durante le operazioni di inserimento, eliminazione e riordinamento. Tuttavia, la domanda sull'utilizzo di key
su componenti singoli merita un'analisi per capire quando e perché potrebbe essere applicabile o meno.
In linea generale, l'uso di key
su un singolo componente non è comune né generalmente necessario, poiché il suo scopo principale è di gestire la stabilità degli identificatori all'interno di collezioni dinamiche. Tuttavia, ci sono scenari specifici anche fuori dalle liste dove key
può trovare un'utilità, specialmente quando si desidera forzare il ri-render o il re-mount di un componente.
- Forzare il Re-mount: A volte potresti voler forzare un componente a ri-montarsi anziché ri-renderizzarsi. Cambiare la
key
di un componente forza React a smontare il componente esistente e a montarne uno nuovo, azzerando così lo stato e gli effetti laterali precedenti. Questo può essere utile in scenari come:- Reset di formulari o componenti complessi che non si resettano correttamente con un semplice aggiornamento dello stato.
- Ricaricamento di un componente quando cambiano props che non sono direttamente collegate allo stato interno o agli effetti del componente.
Supponiamo di avere un componente UserProfile
che mostra informazioni dettagliate di un utente e desideri che il componente si ri-monti completamente ogni volta che l'utente cambia (ad esempio, quando un nuovo utente si logga):
function App() {
const [userId, setUserId] = useState(1);
return (
<>
<UserProfile key={userId} userId={userId} />
<button onClick={() => setUserId(userId + 1)}>Carica Prossimo Utente</button>
</>
);
}
In questo esempio, ogni volta che l'ID utente cambia e quindi cambia la key
, React ri-monterà UserProfile
, resettando completamente qualsiasi stato interno o effetti collaterali associati a quel componente.
-
Prestazioni: L'uso di
key
per forzare il re-mount di componenti può avere implicazioni sulle prestazioni, poiché il re-mounting è un'operazione più costosa del semplice aggiornamento di un componente. È importante valutare se questo approccio è la soluzione migliore o se si possono utilizzare altre tecniche, come reset esplicito dello stato interno del componente. -
Abuso di
key
: L'uso eccessivo di questo metodo può rendere il codice difficile da mantenere e potenzialmente introdurre bug, specialmente in applicazioni più grandi e complesse.
In sintesi, sebbene l'uso primario di key
sia per componenti all'interno di liste, può essere utilizzato strategicamente anche su componenti singoli per gestire casi specifici come il forzato re-mounting. Tuttavia, questa pratica deve essere usata con cautela e comprensione chiara del suo impatto e delle alternative disponibili.
Il hook useState
è uno degli hook fondamentali di React, introdotto nella versione 16.8 che ha portato il concetto di hook nel framework. Questo hook è essenziale per aggiungere lo stato ai componenti funzionali, permettendo di gestire dati che possono cambiare nel tempo all'interno di questi componenti. Prima dell'introduzione degli hook, lo stato poteva essere utilizzato solo nei componenti di classe.
useState
ti permette di aggiungere variabili di stato reattive in componenti funzionali. Quando lo stato cambia, il componente viene ri-renderizzato, permettendo alla UI di riflettere i nuovi valori dello stato.
La sintassi di base di useState
è la seguente:
const [state, setState] = useState(initialState);
initialState
: Il valore iniziale dello stato, che può essere di qualsiasi tipo: un numero, una stringa, un booleano, un array, un oggetto, ecc.state
: La variabile attuale dello stato. Questa variabile cambierà nel tempo quandosetState
viene chiamato.setState
: Una funzione che viene usata per aggiornare lo stato. Quando questa funzione viene chiamata, React ri-renderizzerà il componente con il nuovo stato.
Ecco un esempio pratico di come useState
può essere utilizzato in un componente:
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0); // Inizia il conteggio da zero
return (
<div>
<p>Il conteggio è: {count}</p>
<button onClick={() => setCount(count + 1)}>
Incrementa
</button>
</div>
);
}
In questo esempio, useState
viene usato per tenere traccia del valore di count
. Quando l'utente clicca sul bottone, setCount
viene chiamato con il nuovo valore di count
incrementato di 1, causando il ri-render del componente con il nuovo valore di count
mostrato nella UI.
useState
è particolarmente utile quando hai bisogno di gestire dati dinamici che possono cambiare a causa di interazioni dell'utente, come clic su bottoni, inserimenti di input, switch, ecc., o a seguito di risposte da API esterne o altre fonti di dati.
- Minimizzare i ri-render: Quando usi
useState
, considera l'ottimizzazione dei ri-render. Ad esempio, se il nuovo stato è lo stesso del vecchio stato, React eviterà di ri-renderizzare il componente. - Usa più chiamate di
useState
per variabili di stato diverse: È una pratica comune utilizzare più hookuseState
per gestire variabili di stato differenti invece di un unico oggetto di stato grande, per una maggiore chiarezza e specificità. - Evita calcoli complessi inline in
useState
: Se il valore iniziale dello stato richiede calcoli complessi, considera l'uso di una funzione per inizializzare lo stato, che viene eseguita solo al primo render.
const [state, setState] = useState(() => {
// Calcola un valore iniziale complesso qui
return complexCalculation();
});
useState
è uno strumento estremamente potente e flessibile che apre la porta alla gestione dello stato nei componenti funzionali di React, permettendo una programmazione più espressiva e concisa rispetto all'uso dei classici componenti di classe.
Il hook useReducer
in React è una delle funzioni avanzate che offre un'alternativa a useState
per gestire stati complessi in un componente funzionale. È particolarmente utile quando lo stato logico da gestire ha più valori o quando il prossimo stato dipende da quello precedente in modi più complessi che non possono essere facilmente o chiaramente gestiti con useState
.
useReducer
permette di gestire lo stato del componente attraverso un reducer, una funzione che determina il cambiamento dello stato basandosi su azioni definite. Questo modello è simile a quello utilizzato in Redux, una popolare libreria di gestione dello stato per applicazioni React.
Il hook useReducer
si utilizza generalmente in questo modo:
const [state, dispatch] = useReducer(reducer, initialState);
reducer
: Una funzione che accetta lo stato attuale e un'azione, e ritorna un nuovo stato. È definita come:function reducer(state, action) { switch (action.type) { case 'do-something': return { ...state, ...changes }; // ritorna un nuovo stato basato sull'azione default: throw new Error(); } }
initialState
: Il valore iniziale dello stato usato dal reducer.dispatch
: Una funzione che invii azioni al tuo reducer per aggiornare lo stato.
Ecco un semplice esempio di come si potrebbe utilizzare useReducer
in un componente React:
import React, { useReducer } from 'react';
function counterReducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
return (
<>
Count: {state.count}
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</>
);
}
In questo esempio, useReducer
gestisce lo stato di un contatore. Le azioni 'increment'
e 'decrement'
permettono di modificare il valore del contatore.
- Gestione di stati complessi: È ideale per gestire interazioni di stato più complesse che sono difficili da esprimere con
useState
. - Centralizzazione della logica di modifica dello stato: Il reducer concentra tutta la logica di modifica dello stato in un unico posto, rendendo più facile gestire e capire come cambia lo stato.
- Ottimizzazioni di performance:
useReducer
permette di ottimizzare componenti che eseguono calcoli pesanti o che hanno un alto costo di rendering, in quanto può evitare render inutili.
useReducer
è particolarmente utile quando:
- Lo stato logico del componente è complesso o include più sotto-valori.
- L'aggiornamento dello stato dipende da condizioni o configurazioni complesse.
- Si desidera una gestione dello stato più prevedibile e mantenibile, specialmente in componenti con logica di stato densa o complessa.
In conclusione, useReducer
fornisce un potente strumento per gestire lo stato in applicazioni React, offrendo maggiore controllo su come lo stato è aggiornato e permettendo una gestione più strutturata e modulare dello stesso.
useState
e useReducer
sono due hook di React utilizzati per gestire lo stato nei componenti funzionali. Sebbene entrambi svolgano funzioni simili nel mantenere e aggiornare lo stato, differiscono nelle loro applicazioni ottimali, nel modo in cui manipolano lo stato, e nella complessità dello stato che sono in grado di gestire in modo efficiente.
-
Complessità dello Stato:
useState
è più adatto per gestire stati semplici e indipendenti all'interno del componente, dove le logiche di aggiornamento sono dirette e non troppo intricate.useReducer
è preferibile per gestire stati più complessi o quando lo stato dipende da condizioni precedenti, rendendolo ideale per scenari in cui lo stato logico ha molteplici valori o sub-stati che devono essere aggiornati in risposta a specifiche azioni.
-
Gestione dell'Aggiornamento dello Stato:
useState
fornisce una funzione setter che aggiorna lo stato. Questo setter può essere usato direttamente per impostare il nuovo valore dello stato.useReducer
lavora con un reducer, una funzione che prende lo stato corrente e un'azione, e ritorna un nuovo stato. Ciò rende la logica di aggiornamento più prevedibile e centralizzata, simile a quanto avviene con Redux.
-
Sintassi e Utilizzo:
useState
è generalmente più semplice da scrivere e comprendere, specialmente per i nuovi sviluppatori o per situazioni con logiche di stato meno complesse.useReducer
richiede di definire un reducer e le azioni corrispondenti, aumentando la verbosità del codice, ma fornendo una maggiore struttura, specialmente utile in grandi componenti o applicazioni.
Ecco come potresti utilizzare useState
per un contatore semplice:
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>Incrementa</button>
</div>
);
}
Ecco come lo stesso contatore potrebbe essere implementato con useReducer
:
import React, { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<p>{state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>Incrementa</button>
</div>
);
}
-
Usa
useState
quando:- Lo stato è semplice o indipendente.
- Non hai bisogno di una logica complessa per aggiornare lo stato.
- Vuoi ridurre la complessità del codice.
-
Usa
useReducer
quando:- Hai a che fare con una logica di stato complessa che include diversi sub-stati o quando lo stato successivo dipende fortemente dallo stato attuale in modi non lineari.
- Vuoi una gestione più scalabile e prevedibile dello stato, specialmente utile in componenti grandi con interazioni complesse.
- Vuoi facilitare il testing del comportamento dello stato, poiché il reducer può essere facilmente isolato e testato.
In sintesi, la scelta tra useState
e useReducer
dipende dalla complessità dello stato che stai gestendo e dalla tua preferenza per una struttura di codice più decentralizzata o centralizzata.