Le Context API in React rappresentano un meccanismo efficace per il "prop drilling", ovvero la pratica di passare i dati da un componente all'altro attraverso le props, anche quando ciò non è strettamente necessario. Invece di passare i props a tutti i livelli dell'applicazione, le Context API permettono di condividere valori direttamente tra i componenti, senza doverli passare esplicitamente attraverso ogni livello dell'albero dei componenti. Questo rende molto più semplice la gestione dello stato globale dell'applicazione, specialmente in progetti di grandi dimensioni.
Come Funzionano le Context API
Le Context API funzionano creando un "contexto", che è un modo per condividere i valori tra i componenti senza dover utilizzare le props. Un contesto può essere definito utilizzando React.createContext()
, e ha due parti principali: il Provider e il Consumer.
- Provider: Un componente che fornisce il valore del contesto ai suoi componenti figli. Tutti i componenti figli, indipendentemente da quanto profondamente sono annidati, possono accedere a questo valore senza doverlo ricevere esplicitamente come prop.
- Consumer: Un componente che consuma il valore fornito dal Provider. In alternativa, si può utilizzare l'hook
useContext
per accedere al valore del contesto in componenti funzionali.
Gestione dello stato complessa
Quando si ha a che fare con una logica di stato più complessa, combinare useContext
con useReducer
in React può offrire un approccio più strutturato e scalabile. Questo metodo è particolarmente utile per gestire stati globali che richiedono diverse azioni per essere aggiornati in maniera prevedibile.
Gestione dello Stato di Autenticazione
In questo esempio, creeremo un contesto di autenticazione per un'applicazione, utilizzando useContext
per accedere allo stato e alle azioni di autenticazione, e useReducer
per gestire le modifiche allo stato.
Definizione del Reducer e del Contexto
Primo, definiamo il reducer e il contesto. Creeremo un file AuthContext.tsx
.
1import React, { createContext, useContext, useReducer, ReactNode } from 'react';
2
3type AuthState = {
4 isAuthenticated: boolean;
5};
6
7type AuthAction =
8 | { type: 'LOGIN' }
9 | { type: 'LOGOUT' };
10
11const initialState: AuthState = {
12 isAuthenticated: false,
13};
14
15const AuthContext = createContext<{ state: AuthState; dispatch: React.Dispatch<AuthAction> } | undefined>(undefined);
16
17function authReducer(state: AuthState, action: AuthAction): AuthState {
18 switch (action.type) {
19 case 'LOGIN':
20 return { ...state, isAuthenticated: true };
21 case 'LOGOUT':
22 return { ...state, isAuthenticated: false };
23 default:
24 return state;
25 }
26}
27
28export const AuthProvider = ({ children }: { children: ReactNode }) => {
29 const [state, dispatch] = useReducer(authReducer, initialState);
30
31 return (
32 <AuthContext.Provider value={{ state, dispatch }}>
33 {children}
34 </AuthContext.Provider>
35 );
36};
37
38export const useAuth = () => {
39 const context = useContext(AuthContext);
40 if (!context) {
41 throw new Error('useAuth must be used within an AuthProvider');
42 }
43 return context;
44};
Qui, AuthContext
è il contesto che abbiamo creato, che conterrà lo stato di autenticazione e una funzione dispatch
per eseguire azioni. AuthProvider
è il componente che useremo per avvolgere la parte dell'applicazione che ha bisogno di accedere a queste informazioni. useAuth
è un hook personalizzato che facilita l'accesso al contesto di autenticazione.
Utilizzo del Contexto di Autenticazione
Per utilizzare il contesto, avvolgiamo i nostri componenti con AuthProvider
nel punto più alto possibile dell'albero dei componenti, tipicamente in App.tsx
.
1import React from 'react';
2import { AuthProvider } from './AuthContext';
3import LoginComponent from './LoginComponent';
4
5function App() {
6 return (
7 <AuthProvider>
8 <LoginComponent />
9 </AuthProvider>
10 );
11}
12
13export default App;
E infine, un componente che utilizza useAuth
per effettuare il login e il logout:
1import React from 'react';
2import { useAuth } from './AuthContext';
3
4const LoginComponent = () => {
5 const { state, dispatch } = useAuth();
6
7 return (
8 <div>
9 {state.isAuthenticated ? (
10 <>
11 <p>Sei autenticato!</p>
12 <button onClick={() => dispatch({ type: 'LOGOUT' })}>Logout</button>
13 </>
14 ) : (
15 <>
16 <p>Non sei autenticato.</p>
17 <button onClick={() => dispatch({ type: 'LOGIN' })}>Login</button>
18 </>
19 )}
20 </div>
21 );
22};
23
24export default LoginComponent;
In questo componente, usiamo useAuth
per accedere allo stato di autenticazione e alla funzione dispatch
. I pulsanti permettono all'utente di effettuare il login o il logout, triggerando un cambiamento nello stato globale di autenticazione che si riflette in tutta l'applicazione.
Conclusione
L'uso combinato di useContext
e useReducer
fornisce un potente strumento per gestire lo stato complesso in modo più prevedibile e organizzato. Questo pattern è particolarmente utile per gestire stati globali in applicazioni grandi e complesse, offrendo una chiara separazione tra la logica di gestione dello stato e l'interfaccia utente.
Caricamento...
Diventiamo amici di penna? ✒️
Iscriviti alla newsletter per ricevere una mail ogni paio di settimane con le ultime novità, gli ultimi approfondimenti e gli ultimi corsi gratuiti puubblicati. Ogni tanto potrei scrivere qualcosa di commerciale, ma solo se mi autorizzi, altrimenti non ti disturberò oltre.
Se non ti piace la newsletter ti ricordo la community su Discord, dove puoi chiedere aiuto, fare domande e condividere le tue esperienze (ma soprattutto scambiare due meme con me). Ti aspetto!