Nomenclatura consistente en Redux
Los action types en Redux siguen el patrón domain/ACTION_NAME o DOMAIN_ACTION_NAME. Con Redux Toolkit, esto se simplifica: createSlice genera automáticamente action creators con nombres tipo sliceName/reducerName. El error más común: mezclar convenciones (camelCase vs UPPER_SNAKE_CASE) en el mismo proyecto.
Estructura recomendada: un slice por feature, no por tipo de dato. Ejemplo: cartSlice maneja items, totals, discounts (todo cart-related), no itemsSlice + totalsSlice separados. Esto reduce boilerplate y facilita refactoring.
Para async actions con thunks, el patrón estándar es fetchUsers (thunk) que dispara fetchUsersPending, fetchUsersFulfilled, fetchUsersRejected. Redux Toolkit los genera automáticamente con createAsyncThunk. No inventar variantes como fetchUsersStart o fetchUsersSuccess.
Patrones de actions según caso de uso
CRUD básico usa 5 actions mínimas: fetch (load data), add/create (POST), update/edit (PUT/PATCH), delete/remove (DELETE), y set (reemplazar state completo). UI state necesita toggle, open, close, reset para modals, sidebars, dropdowns.
Formularios requieren setFieldValue (individual), setFieldError (validación), resetForm (limpiar), y submitForm (async). Wizard forms agregan nextStep, previousStep, goToStep. Auto-save usa saveDraft + debounce.
Realtime con WebSocket: connectSocket (initiate), socketConnected (success), socketError (fail), socketReconnect (retry), disconnectSocket (cleanup). Actions de presence: userJoined, userLeft, updatePresence (typing indicators, online status).
Manejar side effects y async logic
Redux Toolkit recomienda createAsyncThunk para cualquier lógica async. Ejemplo típico: fetchUsers hace GET, dispara pending → fulfilled/rejected automáticamente. En el slice, extraReducers escucha estos actions. Evitar lógica async directamente en reducers (son pure functions).
Patrones avanzados: optimistic updates (update UI antes de confirmar server, rollback si falla), polling (refetch cada N segundos con setInterval en thunk), retry logic (reintentar request fallido con exponential backoff). Ejemplo: payment thunk reintenta 3 veces antes de fallar.
Error handling: guardar error messages en state (error: string | null), no solo booleanos. Actions rejected deben incluir action.error.message para debugging. Para múltiples requests simultáneos, usar Promise.allSettled en lugar de Promise.all (no rompe si uno falla).
Organización de slices en proyectos grandes
Estructura de carpetas recomendada: /features/[feature]/[feature]Slice.ts + [feature]Thunks.ts + [feature]Selectors.ts. Ejemplo: /features/auth/authSlice.ts contiene login, logout, session. No mezclar todos los slices en /store/index.ts.
Usar configureStore con combineReducers implícito. Para state normalizado (evitar nested objects), usar createEntityAdapter: maneja ids, entities, CRUD automático. Ejemplo: usersAdapter.addOne, usersAdapter.updateMany son más eficientes que spread manual.
Selectores con createSelector (memoizados) previenen re-renders innecesarios. Ejemplo: selectActiveUsers = createSelector([selectUsers], users => users.filter(u => u.active)). Si el state de users no cambió, devuelve mismo array. Testing: mockeá store con configureStore({ reducer, preloadedState }), no uses el store real en tests.