Vai al contenuto principale

React e Redux: concetti base

Scopri come utilizzare Redux e Redux Toolkit per la gestione dello stato in React con TypeScript: esempi pratici, vantaggi e configurazione.

Redux è una libreria open-source di JavaScript per la gestione dello stato delle applicazioni. È particolarmente utile per applicazioni con architetture complesse e molti dati dinamici che necessitano di essere sincronizzati tra diverse parti dell'applicazione, come per esempio in progetti basati su React.

Basi di Redux

Redux si basa su tre principi fondamentali:

  1. Single source of truth: Lo stato dell'intera applicazione è conservato all'interno di un albero di oggetti all'interno di un unico store. Questo rende più facile il debug e l'ispezione dello stato.
  2. Stato è di sola lettura: L'unico modo per modificare lo stato è emettere un'azione, che è un oggetto che descrive cosa è successo.
  3. Le modifiche vengono fatte con funzioni pure: Per specificare come lo stato dell'applicazione cambia in risposta ad un'azione, si usano dei reducer, che sono funzioni pure che prendono lo stato precedente e un'azione, e restituiscono il nuovo stato.

Componenti chiave di Redux

  • Store: Contiene lo stato dell'applicazione.
  • Action: Un oggetto che descrive una modifica nello stato.
  • Reducer: Una funzione che ritorna il nuovo stato basato su (stato corrente, azione).

Differenza tra Redux e Context API

L'uso di Redux rispetto alle Context API in applicazioni React è una scelta architetturale che dipende da vari fattori, tra cui la complessità dell'applicazione, le esigenze di gestione dello stato e le preferenze del team di sviluppo. Ecco alcuni motivi per cui si potrebbe preferire Redux alle Context API:

1. Gestione dello Stato Prevedibile

Redux offre un modello di gestione dello stato altamente prevedibile grazie ai suoi tre principi fondamentali. Questo rende più facile ragionare sull'applicazione, soprattutto quando diventa grande e complessa. Redux facilita anche il debugging, grazie a strumenti come Redux DevTools, che permettono di visualizzare, tracciare e manipolare lo stato dell'applicazione in tempo reale.

2. Middleware e Estendibilità

Redux supporta l'uso di middleware, che consente di inserire logica personalizzata durante il dispatching delle azioni. Questo è particolarmente utile per operazioni asincrone, logging, crash reporting e altre funzionalità che necessitano di intercettare o modificare le azioni prima che raggiungano i reducer. Le Context API, sebbene possano essere usate in combinazione con useReducer per una certa estendibilità, non offrono nativamente lo stesso livello di supporto per middleware.

3. Scalabilità

Redux scala bene con applicazioni di grandi dimensioni e complesse, fornendo un framework consistente e prevedibile per la gestione dello stato. La sua architettura incentrata sull'unica fonte di verità e sull'immutabilità dello stato facilita la gestione di grandi quantità di dati e il coordinamento tra componenti lontani nell'albero dei componenti. Le Context API possono causare rerender non necessari in consumatori profondi nell'albero dei componenti se non gestite attentamente, specialmente in app grandi.

4. Ecosistema e Comunità

Redux ha un vasto ecosistema e una comunità di sviluppatori molto attiva. Ci sono molte librerie e strumenti complementari che possono essere integrati con Redux per estenderne le funzionalità. Inoltre, la vasta documentazione, le guide e le risorse di apprendimento disponibili per Redux possono facilitare l'adozione e la risoluzione dei problemi.

5. Pattern e Pratiche Consigliate

Redux incoraggia lo sviluppo seguendo pattern e pratiche consigliate ben definite, che possono aiutare i team di sviluppo a scrivere codice più organizzato e manutenibile. L'architettura di Redux promuove anche la separazione delle preoccupazioni, rendendo più chiaro dove e come le modifiche allo stato dovrebbero essere gestite.

Mentre Redux offre questi vantaggi, non è sempre la scelta giusta per ogni progetto. Per applicazioni piccole o semplici, l'uso delle Context API può essere sufficiente e più diretto. Le Context API sono anche più leggere e meno invasive, dato che sono integrate direttamente in React. La scelta tra Redux e Context API dipende quindi dalle esigenze specifiche del progetto, dalla complessità dello stato da gestire e dalle preferenze del team di sviluppo.

Esempio pratico di Redx

Giunti a questo punto di teoria ne abbiamo accumulata abbastanza, diamoci da fare con un esempio pratico.

Prima di tutto, installiamo Redux Toolkit e React-Redux:

npm install @reduxjs/toolkit react-redux

Configurazione dello Store con Redux Toolkit

Redux Toolkit fornisce la funzione configureStore per semplificare la configurazione dello store. Creiamo un file store.ts utilizzando Redux Toolkit:

// store.ts
import { configureStore, createSlice, PayloadAction } from '@reduxjs/toolkit';

// Definizione dello stato iniziale
type TodoState = string[];

// Creazione di uno slice che include reducer e azioni
const todosSlice = createSlice({
  name: 'todos',
  initialState: [] as TodoState,
  reducers: {
    addTodo: (state, action: PayloadAction<string>) => {
      state.push(action.payload);
    },
		removeTodo: (state, action: PayloadAction<number>) => {
			state.splice(action.payload, 1);
		}
  },
});

export const { addTodo, removeTodo } = todosSlice.actions;

const store = configureStore({
  reducer: {
    todos: todosSlice.reducer,
  },
});

// Tipizzazione dello stato globale
export type RootState = ReturnType<typeof store.getState>;

export default store;

Creazione di Componenti React con React-Redux

Aggiorniamo il componente App per utilizzare useDispatch e useSelector da React-Redux insieme agli slice di Redux Toolkit:

// App.tsx
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { RootState, addTodo, removeTodo } from './redux/store';

const App: React.FC = () => {
  const [todo, setTodo] = useState('');
  const todos = useSelector((state: RootState) => state.todos);
  const dispatch = useDispatch();

  const handleAddTodo = () => {
    dispatch(addTodo(todo));
    setTodo('');
  };

	const handleRemoveTodo = (index: number) => {
		dispatch(removeTodo(index));
	}

  return (
    <div>
      <input type="text" value={todo} onChange={(e) => setTodo(e.target.value)} />
      <button onClick={handleAddTodo}>Add Todo</button>
      <ul>
        {todos.map((todo, index) => (
          <li key={index}>{todo} <button onClick={() => handleRemoveTodo(index)}>Elimina</button></li>
        ))}
      </ul>
    </div>
  );
};

export default App;
// index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import store from './store';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

Esecuzione dell'Applicazione

Dopo aver aggiornato i file come indicato sopra, puoi eseguire l'applicazione con il seguente comando, assumendo che tu stia utilizzando Create React App o una configurazione simile:

npm start

Questo approccio moderno con Redux Toolkit e React-Redux semplifica la configurazione dello store, la definizione di reducer e azioni, e l'integrazione con componenti React, rendendo il codice più leggibile e manutenibile.

Unisciti a WebTea

Niente spam. Solo contenuti formativi su Software Engineering.

Ci sono due cose che non ci piacciono: lo spam e il mancato rispetto della privacy. Seleziona come vuoi restare in contatto:

Preferenze di contatto

Usiamo Mailchimp come piattaforma di marketing. Cliccando su iscriviti, accetti la nostra privacy policy e che le tue informazioni vengano trasferite a Mailchimp per l'elaborazione. Termini e Privacy. Puoi disiscriverti in qualsiasi momento.