Migrando un applicazione React 16 alla versione 18, ho voluto minimizzare la configurazione di Redux, passando una configurazione tradizionale ad una con react redux toolkit.
Uno dei problemi che si incontrano nella configurazione “standard” di Redux è la sua complessità.
E’ necessario con una serie di file, e la complessità di configurazione aumenta all’aumentare della complessità dell’applicazione.
Uno degli obiettivi di redux toolkit è proprio quello di ottimizzare e semplificare la configurazione di redux all’interno dell’applicazione.
Il primo step consiste nell’installazione tramite il comando:
npm install @reduxjs/toolkit
Nel caso della mia applicazione è già presente redux, ma se non fosse presente è necessario installarlo utilizzando il comando:
npm install redux
Iniziamo con la creazione dello store che in react toolkit viene effettuata utilizzando la funzione configureStore, da inserire all’interno del file src/store.ts (da creare).
import { configureStore } from "@reduxjs/toolkit";
export const store = configureStore({});
Viene in pratica definito lo store, ma non vengono ancora aggiungere i reducers che devono ancora essere definiti.
In Redux Toolkit viene introdotto il concetto di slices, ovvero la porzione di codice che consente di creare in un unico punto action, reducer e inizializzazione. Lo slice viene creato utilizzando la funzione createSlice.
Supponiamo di voler creare uno slice per la gestione del profilo di un utente. Creiamo una cartella all’interno di src e chiamiamolo slices. All’interno della cartella creiamo un file denominato profile.ts:
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
const initialState = {
email: '',
};
type TPayload = {
email: string;
};
const profileSlice = createSlice({
name: 'profile',
initialState: initialState,
reducers: {
changeEmail: (state, { payload }: PayloadAction<TPayload>) => {
state.email = payload.email;
},
},
});
export const profileReducer = profileSlice.reducer;
export const { changeEmail } = profileSlice.actions;La funzione createSlice accetta come parametri:
- name: il nome dello slice che stiamo definendo
- initialState: la funzione che si occupa di inizializzare il reducer
- reducers: la definizione delle actions
Inoltre per semplificare l’accesso al reducer ed alle action vengono esportate le due constanti profileReducer (o il nome del reducer che vogliamo esportare) e l’elenco delle actions disponibili.
Utilizzando typescript nell’applicazione di partenza, ho definito il tipo di Paylod che dovrà essere utilizzato (nel mio caso TPayload) che è semplicemente l’indirizzo email che dovrà essere variato all’interno del reducer. Da notare l’utilizzo del parametro di tipo PayloadAction<TPayLoad> che viene utilizzato per passare il nuovo valore da assegnare allo state.
A questo punto, probabilmente, sorgerà una domanda: come viene gestita l’immutabilità? Utilizzando il redux in maniera “tradizionale” era necessario ad esempio utilizzare l’operatore spread per il salvataggio dello state e la modifica della sola parte che doveva essere modificata. React Toolkit integra al suo interno tutto quello che si rende necessario per la gestione dell’immutabilità, e l’unico compito dell’utente è quello di modificare il valore della variabile da settare. Un bel risparmio in termine di codice e leggibilità .
A questo punto abbiamo creato il nostro primo slice, non ci resta che creare la definizione del reducer come aggregazione di piu reducer. Creiamo quindi una cartella all’interno di src e la chiamiamo reducers. All’interno un file denominato reducer.ts:
import { profileReducer } from "../slices/profile";
const reducer = {
profileReducer,
// another reducers (if we have)
};
export default reducer;Si tratta, in pratica, di un file all’interno del quale verranno aggiunti i riferimenti di tutti i reducer presenti all’interno dell’applicazione. Questo tipo di approccio mi serve per poter effettuare la creazione dello state partendo dalla definizione di tutti i reducers (che verranno aggregati).
A questo punto passiamo alla creazione dello store, modificando il file store.ts creato in precedenza:
import { configureStore } from "@reduxjs/toolkit";
import reducer from "./reducer/reducer";
export const store = configureStore({ reducer });
export type TStore = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatchDa notare l’export del getState del reducer e del dispach.
A questo punto siamo pronti per iniettare lo store all’interno dell’applicazione, modificando il file index.tsx, aggiungendo la defizione del Provider:
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<Provider store={store}>
<App />
</Provider>
);A questo punto lo store è a disposizione dell’intera applicazione.
Proviamo quindi a generare un component ed eseguire il dispatch. Dall cli eseguiamo il comando:
npx generate-react-cli component Box
che genera il componente denominato Box.
import { useDispatch, useSelector } from "react-redux";
import { TStore } from '../../store';
import { changeEmail } from '../../slices/profile';
const Box = () => {
const dispatch = useDispatch();
const handleEmailChange = () => {
dispatch(changeEmail({ email: "newEmail@gmail.com" }));
};
return (
<button onClick={handleEmailChange}>
Change email
</button>
);
};
export default Box;