In questo articolo costruiremo un carrello e-commerce con React Redux e Redux Toolkit, seguendo le pratiche consigliate.
Perché usare Redux Toolkit con React Redux
- API moderne e concise (createSlice, configureStore) riducono il boilerplate.
- Performance e DX: integrazione con DevTools, middleware preconfigurati e tipizzazione facilitata.
- Compatibilità aggiornata: React Redux è allineato a React e a build ESM/CJS moderne.
Installazione
npm install @reduxjs/toolkit react-reduxStruttura minima
- src/app/store.js— configurazione dello store
- src/features/cart/cartSlice.js— stato e reducer del carrello
- src/App.jsx— UI (lista prodotti + carrello)
- src/main.jsx— bootstrap React con- <Provider>
Slice del carrello
// src/features/cart/cartSlice.js
import { createSlice, createSelector } from '@reduxjs/toolkit';
const initialState = {
// mappa id => { id, name, price, qty }
  items: {}
};
const cartSlice = createSlice({
name: 'cart',
initialState,
reducers: {
  addedToCart(state, action) {
    const p = action.payload; // {id, name, price}
    const curr = state.items[p.id];
    state.items[p.id] = curr
    ? { ...curr, qty: curr.qty + 1 }
    : { ...p, qty: 1 };
  },
  removedFromCart(state, action) {
    delete state.items[action.payload]; // payload = productId
  },
  changedQty(state, action) {
    const { id, qty } = action.payload;
    if (state.items[id]) {
      state.items[id].qty = Math.max(1, qty);
    }
  },
  clearedCart(state) {
    state.items = {};
  }
}
});
export const { addedToCart, removedFromCart, changedQty, clearedCart } = cartSlice.actions;
export default cartSlice.reducer;
// --- Selectors ---
const selectCartState = (root) => root.cart;
export const selectItemsArray = createSelector(
  selectCartState,
  (cart) => Object.values(cart.items)
);
export const selectCount = createSelector(selectItemsArray, (arr) =>
  arr.reduce((sum, it) => sum + it.qty, 0)
);
export const selectTotal = createSelector(selectItemsArray, (arr) =>
  arr.reduce((sum, it) => sum + it.qty * it.price, 0)
); Store Redux
// src/app/store.js
import { configureStore } from '@reduxjs/toolkit';
import cartReducer from '../features/cart/cartSlice';
export const store = configureStore({
  reducer: {
    cart: cartReducer
  }
});
// Tip: in DevTools vedrai le azioni e lo stato senza configurazioni extra Bootstrap
// src/main.jsx
import React from 'react';
import { createRoot } from 'react-dom/client';
import { Provider } from 'react-redux';
import { store } from './app/store';
import App from './App';
createRoot(document.getElementById('root')).render(
<React.StrictMode>
  <Provider store={store}>
    <App />
  </Provider>
</React.StrictMode>
); UI di esempio: lista prodotti + carrello
// src/App.jsx
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {
  addedToCart,
  removedFromCart,
  changedQty,
  clearedCart,
  selectItemsArray,
  selectCount,
  selectTotal
} from './features/cart/cartSlice';
const products = [
  { id: 'p1', name: 'T-shirt', price: 19.9 },
  { id: 'p2', name: 'Sneakers', price: 79.0 },
  { id: 'p3', name: 'Zaino', price: 49.5 }
];
export default function App() {
  const dispatch = useDispatch();
  const items = useSelector(selectItemsArray);
  const count = useSelector(selectCount);
  const total = useSelector(selectTotal);
return (
<>
<h1>E-commerce Demo</h1>
  <h2>Prodotti</h2>
  {products.map(p => (
    <figure key={p.id}>
      <figcaption>{p.name} — € {p.price.toFixed(2)}</figcaption>
      <button onClick={() => dispatch(addedToCart(p))}>Aggiungi al carrello</button>
    </figure>
  ))}
  <h2>Carrello ({count})</h2>
  {items.length === 0 ? (
    <p>Il tuo carrello è vuoto.</p>
  ) : (
    <ul>
      {items.map(it => (
        <li key={it.id}>
          <strong>{it.name}</strong> — € {it.price.toFixed(2)}
           × 
          <input
            type="number"
            min="1"
            value={it.qty}
            onChange={(e) =>
              dispatch(changedQty({ id: it.id, qty: Number(e.target.value) }))
            }
            style={{ width: 56 }}
          />
           
          <button onClick={() => dispatch(removedFromCart(it.id))}>Rimuovi</button>
        </li>
      ))}
    </ul>
  )}
  <p><strong>Totale:</strong> € {total.toFixed(2)}</p>
  <button onClick={() => dispatch(clearedCart())}>Svuota carrello</button>
</>
);
} Note importanti
- React 18 obbligatorio per React Redux 9 (niente supporto a React 16/17).
- Versioni attuali: React Redux 9.2.0 su npm; Redux Toolkit 2.9.0 su GitHub. Controlla sempre i changelog prima di aggiornare.
- Starter consigliati: template ufficiale Redux+TS per Vite o Next.js con Redux preconfigurato.
Estensioni utili
- Persistenza: salva lo stato del carrello su localStoragecon un middleware custom.
- RTK Query: gestisci il catalogo prodotti remoto (fetch, cache, polling) separando “server state” dal carrello.
Conclusione
Con React Redux e Redux Toolkit ottieni uno store semplice da configurare, reducer espressivi e selettori performanti. La base mostrata qui è pronta per crescere: aggiungi autenticazione, persistenza, pagamenti e integrazioni API mantenendo uno stato globale prevedibile e testabile.