Redux Toolkit: Moderne Patterns mit RTK Query und createSlice
AI generated
</>
{ }
React · Redux Toolkit · RTK Query · State Management
Redux Toolkit: Moderne Patterns
mit RTK Query und createSlice

Redux Toolkit hat klassisches Redux mit seinen endlosen Switch Statements und Action Type Konstanten weitgehend abgelöst. Mit createSlice, RTK Query und Entity Adapter lässt sich globaler State in React Anwendungen mit deutlich weniger Code, dafür mit mehr Typsicherheit und eingebautem Server State Caching abbilden.

18 Min. Lesezeit createSlice · RTK Query · Entity Adapter · createSelector Redux Toolkit 2.x · React 19 · TypeScript

1. Warum Redux Toolkit und nicht klassisches Redux

Klassisches Redux verlangte für jeden noch so kleinen State drei Action Type Konstanten, mindestens einen Action Creator und einen Switch Statement Reducer mit manuell geschriebener Immutability Logik. Redux Toolkit wurde von den Redux Maintainern selbst entwickelt, um genau dieses Boilerplate zu eliminieren, ohne die Grundidee von Redux, einem einzigen vorhersagbaren Store mit unidirektionalem Datenfluss, aufzugeben. Seit einigen Jahren gilt Redux Toolkit als der offiziell empfohlene Weg, Redux überhaupt zu verwenden, klassisches Redux ohne Toolkit gilt inzwischen als Legacy Pattern.

Der Kern von Redux Toolkit besteht aus configureStore, createSlice und createAsyncThunk, ergänzt durch das separate Paket RTK Query für Server State. Unter der Haube nutzt Redux Toolkit die Bibliothek Immer, wodurch Reducer Funktionen so aussehen dürfen, als würden sie den State direkt mutieren, obwohl im Hintergrund weiterhin ein neuer, unveränderlicher State erzeugt wird. Für Teams, die bereits mit Redux arbeiten und auf React 19 migrieren, ist Redux Toolkit meist der pragmatischste Weg, weil der Store selbst erhalten bleibt und nur die Schreibweise der Slices modernisiert wird.

Ein oft übersehener Vorteil von Redux Toolkit: die DevTools Integration ist bereits vorkonfiguriert, ebenso sinnvolle Middleware Defaults wie die Serializable Check und die Immutability Check, die in der Entwicklung automatisch Verstöße gegen die Redux Grundregeln erkennen. Wer diese Warnungen ernst nimmt, verhindert subtile Bugs, die in klassischem Redux oft erst in Produktion auffallen.

2. createSlice: Reducer Logik ohne Boilerplate

createSlice ist die zentrale Funktion von Redux Toolkit und fasst Action Types, Action Creators und Reducer in einer einzigen Deklaration zusammen. Statt für jede Aktion einen eigenen String zu definieren, generiert createSlice die Action Types automatisch aus dem Slice Namen und den Reducer Funktionsnamen. Innerhalb der Reducer Funktionen erlaubt Immer die direkte Zuweisung an Objekteigenschaften und Array Push Operationen, was den Code deutlich näher an normalen JavaScript Funktionen hält als das reine Redux Pattern mit Spread Operatoren.

Ein zweiter wichtiger Baustein ist prepare, mit dem sich die Payload einer Aktion vor dem eigentlichen Reducer transformieren lässt, etwa um eine eindeutige ID zu generieren, bevor ein Eintrag in den State geschrieben wird. Für asynchrone Abläufe, die nicht über RTK Query laufen, lassen sich Extra Reducer über extraReducers an einen Slice anhängen, sodass ein Slice auf die Pending, Fulfilled und Rejected Zustände eines separat definierten Thunks reagieren kann, ohne dass der Thunk selbst im Slice definiert sein muss.


// features/cartSlice.js — createSlice replaces action types, creators and reducer boilerplate
import { createSlice } from '@reduxjs/toolkit';

const cartSlice = createSlice({
  name: 'cart',
  initialState: { items: [], total: 0 },
  reducers: {
    itemAdded: (state, action) => {
      // Immer lets us "mutate" state directly, a new immutable state is created behind the scenes
      const existing = state.items.find((item) => item.id === action.payload.id);
      if (existing) {
        existing.quantity += 1;
      } else {
        state.items.push({ ...action.payload, quantity: 1 });
      }
      state.total = state.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
    },
    itemRemoved: (state, action) => {
      state.items = state.items.filter((item) => item.id !== action.payload);
      state.total = state.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
    },
    cartCleared: (state) => {
      state.items = [];
      state.total = 0;
    },
  },
});

export const { itemAdded, itemRemoved, cartCleared } = cartSlice.actions;
export default cartSlice.reducer;

3. RTK Query: Server State ohne manuelle Caching Logik

RTK Query ist der wohl größte Sprung, den Redux Toolkit gegenüber klassischem Redux gemacht hat. Statt Thunks für jeden API Aufruf manuell zu schreiben, inklusive Loading Flags, Error Handling und Caching, definiert man mit createApi eine deklarative API Slice, aus der automatisch typisierte React Hooks generiert werden. RTK Query übernimmt Request Deduplizierung, Cache Invalidation über Tags und automatisches Refetching, sobald sich abhängige Daten ändern, alles ohne zusätzliche Bibliothek.

Das Tag basierte Invalidation System ist dabei das mächtigste Konzept: Jede Query kann Tags bereitstellen, jede Mutation kann dieselben Tags invalidieren, und RTK Query triggert automatisch ein Refetch aller betroffenen Queries. Für Teams, die bereits Redux im Einsatz haben, ersetzt RTK Query damit häufig separate Data Fetching Bibliotheken, weil der Server State im selben Store landet wie der Client State und mit denselben DevTools inspiziert werden kann.


// features/apiSlice.js — RTK Query replaces manual thunks and caching logic
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const productsApi = createApi({
  reducerPath: 'productsApi',
  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
  tagTypes: ['Product'],
  endpoints: (builder) => ({
    getProducts: builder.query({
      query: () => 'products',
      providesTags: (result) =>
        result
          ? [...result.map(({ id }) => ({ type: 'Product', id })), { type: 'Product', id: 'LIST' }]
          : [{ type: 'Product', id: 'LIST' }],
    }),
    updateProduct: builder.mutation({
      query: ({ id, ...patch }) => ({ url: `products/${id}`, method: 'PATCH', body: patch }),
      // Invalidating this tag triggers an automatic refetch of getProducts
      invalidatesTags: (result, error, { id }) => [{ type: 'Product', id }],
    }),
  }),
});

export const { useGetProductsQuery, useUpdateProductMutation } = productsApi;

4. Selektoren mit createSelector und Memoization

Ohne Memoization berechnet jeder useSelector Aufruf abgeleitete Daten bei jedem Render neu, selbst wenn sich die relevanten Eingabedaten nicht geändert haben. createSelector aus Redux Toolkit löst das über Memoization: Der Selektor merkt sich das letzte Ergebnis und berechnet nur neu, wenn sich mindestens eine der Input Selektoren geänderten Wert liefert. Bei komplexen Ableitungen wie gefilterten und sortierten Listen ist das der Unterschied zwischen einem Rerender in Mikrosekunden und einem spürbaren Ruckler bei größeren Datenmengen.

Ein häufiger Fehler ist, Selektoren mit neuen Objektreferenzen als Rückgabewert zu schreiben, etwa state => ({ items: state.cart.items }). Dieses Objekt Literal erzeugt bei jedem Aufruf eine neue Referenz, wodurch React Redux jeden Render als Änderung interpretiert und die Memoization von createSelector wirkungslos wird. Die Lösung ist, entweder primitive Werte zurückzugeben oder das gesamte abgeleitete Objekt selbst über createSelector zu memoizieren, sodass nur bei echter Änderung eine neue Referenz entsteht.


// features/cartSelectors.js — createSelector memoizes derived state
import { createSelector } from '@reduxjs/toolkit';

const selectCartItems = (state) => state.cart.items;
const selectCategoryFilter = (state) => state.filters.category;

// Recomputes only when selectCartItems or selectCategoryFilter actually change
export const selectFilteredCartTotal = createSelector(
  [selectCartItems, selectCategoryFilter],
  (items, category) =>
    items
      .filter((item) => !category || item.category === category)
      .reduce((sum, item) => sum + item.price * item.quantity, 0)
);

5. Entity Adapter: Normalisierte Daten verwalten

Listen von Entitäten, etwa Produkte, Benutzer oder Bestellungen, werden in vielen Redux Stores naiv als Array gespeichert. Das führt bei Updates einzelner Einträge zu linearem Suchaufwand und bei ID basiertem Zugriff zu unnötig komplexem Code. createEntityAdapter normalisiert solche Collections automatisch in die Form { ids: [], entities: {} }, wodurch der Zugriff auf ein einzelnes Element per ID zu einer konstanten Objektlookup Operation wird statt einer linearen Suche durch ein Array.

Entity Adapter generiert zusätzlich vorgefertigte Reducer Funktionen wie addOne, updateOne, removeMany und upsertMany, die sich direkt in die reducers Definition eines createSlice Aufrufs einhängen lassen. In Kombination mit RTK Query lässt sich der Adapter sogar direkt in der transformResponse Funktion einer Query einsetzen, sodass Serverdaten bereits normalisiert im Cache landen, bevor eine Komponente sie über einen generierten Selektor abruft.


// features/productsSlice.js — Entity Adapter normalizes list state
import { createSlice, createEntityAdapter } from '@reduxjs/toolkit';

const productsAdapter = createEntityAdapter({
  sortComparer: (a, b) => a.name.localeCompare(b.name),
});

const productsSlice = createSlice({
  name: 'products',
  initialState: productsAdapter.getInitialState({ status: 'idle' }),
  reducers: {
    productUpserted: productsAdapter.upsertOne,
    productsReceived: productsAdapter.upsertMany,
    productRemoved: productsAdapter.removeOne,
  },
});

// Generated selectors give O(1) lookups instead of array scans
export const {
  selectAll: selectAllProducts,
  selectById: selectProductById,
  selectIds: selectProductIds,
} = productsAdapter.getSelectors((state) => state.products);

export const { productUpserted, productsReceived, productRemoved } = productsSlice.actions;
export default productsSlice.reducer;

6. Async Flows: createAsyncThunk versus RTK Query

Bevor RTK Query existierte, war createAsyncThunk der Standardweg für asynchrone Logik in Redux Toolkit, und für Fälle jenseits von reinem Data Fetching bleibt es relevant, etwa für mehrstufige Workflows, Datei Uploads mit Fortschrittsanzeige oder Aktionen, die mehrere API Aufrufe orchestrieren. createAsyncThunk generiert automatisch drei Action Types, Pending, Fulfilled und Rejected, auf die ein Slice über extraReducers reagieren kann, um Ladezustand, Ergebnis und Fehler im State abzubilden.

Für reines Server State Management, also das Laden, Cachen und Aktualisieren von Daten von einer REST oder GraphQL Schnittstelle, ist RTK Query heute die deutlich bessere Wahl, weil es Deduplizierung, Cache Invalidation und Polling ohne zusätzlichen Code mitbringt. Die Faustregel in modernen Redux Toolkit Projekten lautet: RTK Query für alles, was aus einer Datenquelle kommt und synchronisiert werden muss, createAsyncThunk für Business Logik, die zwar asynchron ist, aber keinen klassischen Cache benötigt.

7. TypeScript Integration: Typsichere Slices und Hooks

Redux Toolkit wurde von Grund auf mit TypeScript im Kopf entworfen, und createSlice leitet die meisten Typen automatisch aus dem initialState Objekt ab, sodass in den Reducer Funktionen kaum manuelle Typannotationen nötig sind. Für Actions mit Payload reicht PayloadAction<Type> als Typ für den Action Parameter, wodurch der Compiler bereits beim Schreiben des Reducers auf falsche Payload Formen hinweist, lange bevor ein Test fehlschlägt.

Für den Store selbst empfiehlt sich, RootState und AppDispatch aus configureStore abzuleiten und typisierte Versionen von useSelector und useDispatch zu exportieren. Dieses Pattern verhindert, dass in jeder Komponente der State Typ manuell angegeben werden muss, und stellt sicher, dass Refactorings im Store sofort als Compile Fehler in allen betroffenen Komponenten sichtbar werden, statt erst zur Laufzeit als undefined Fehler aufzutauchen.


// app/store.ts — typed store setup for React and Redux Toolkit
import { configureStore } from '@reduxjs/toolkit';
import cartReducer from '../features/cartSlice';
import { productsApi } from '../features/apiSlice';

export const store = configureStore({
  reducer: {
    cart: cartReducer,
    [productsApi.reducerPath]: productsApi.reducer,
  },
  middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(productsApi.middleware),
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

// hooks.ts — typed hooks, use these everywhere instead of plain useSelector/useDispatch
import { useDispatch, useSelector, type TypedUseSelectorHook } from 'react-redux';

export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

8. Middleware und DevTools: Debugging und Erweiterbarkeit

configureStore aktiviert standardmäßig eine Reihe von Middleware Checks, die in der Entwicklung typische Redux Fehler frühzeitig sichtbar machen. Die Immutability Middleware erkennt versehentliche direkte State Mutationen außerhalb von Immer Kontexten, die Serializable Check warnt, wenn nicht serialisierbare Werte wie Klassen Instanzen oder Promises im State oder in Actions landen, was spätere Persistierung oder Time Travel Debugging unmöglich machen würde.

Für eigene Querschnittsanliegen, etwa zentrales Logging, Analytics Tracking oder das automatische Anhängen von Auth Tokens an bestimmte Actions, lässt sich eigene Middleware über getDefaultMiddleware().concat(customMiddleware) ergänzen. Die Redux DevTools Extension ist bei configureStore standardmäßig aktiviert und erlaubt Time Travel Debugging, das Zurückspulen einzelner Actions und das Exportieren kompletter Action Historien, was bei der Reproduktion schwer greifbarer Bugs enorm hilft.

9. Redux Toolkit im Vergleich zu anderen Ansätzen

Redux Toolkit konkurriert heute weniger mit klassischem Redux, das gilt inzwischen als überholt, sondern mit leichteren Bibliotheken wie Zustand oder Jotai sowie mit reinem Server State Tooling wie TanStack Query. Die folgende Tabelle ordnet Redux Toolkit entlang der Kriterien ein, die in der Praxis über die Wahl entscheiden.

Kriterium Redux Toolkit Zustand TanStack Query allein
Boilerplate pro Feature Gering dank createSlice, aber Store Setup nötig Minimal, ein Hook pro Store Minimal, nur für Server State
Server State Caching Eingebaut über RTK Query Nicht eingebaut, externe Lösung nötig Kernfunktion der Bibliothek
DevTools und Time Travel Vollständig, produktionsreif Rudimentär über Middleware Eigenes, auf Queries fokussiertes Panel
Eignung bei großen Teams Konventionen erzwingen Konsistenz Erfordert eigene Konventionen Gut, aber nur für Server State
Lernkurve Höher, Store, Slices, Provider Sehr niedrig Niedrig für Data Fetching

In der Praxis entscheidet meist die Teamgröße und die Menge an geteiltem Client State: Kleine Teams mit wenig komplexem Client State fahren oft besser mit Zustand plus TanStack Query, während große Teams mit vielen Slices von den erzwungenen Redux Toolkit Konventionen und den ausgereiften DevTools profitieren.

Mironsoft

React Architektur, State Management und moderne Frontend Infrastruktur

Redux Store der wächst statt zu bremsen?

Wir analysieren bestehende Redux Stores, migrieren Legacy Redux auf Redux Toolkit und führen RTK Query für euren Server State ein, mit sauberen Slices, typsicheren Selektoren und DevTools freundlicher Architektur.

Redux Migration

Legacy Redux auf createSlice und Entity Adapter migrieren, ohne Big Bang Rewrite

RTK Query Einführung

Manuelle Thunks durch deklarative API Slices mit Cache Invalidation ersetzen

TypeScript Hardening

Typisierte Hooks, RootState Ableitung und Compile Zeit Sicherheit im gesamten Store

10. Zusammenfassung

Redux Toolkit löst das klassische Redux Boilerplate Problem durch createSlice, das Action Types, Action Creators und Reducer in einer einzigen Deklaration bündelt und dank Immer direkte Mutationen im Reducer Code erlaubt. RTK Query übernimmt Server State Management vollständig, inklusive Deduplizierung, Tag basierter Cache Invalidation und automatischem Refetching, wodurch separate Data Fetching Bibliotheken oft überflüssig werden. Entity Adapter normalisiert Listen Daten für konstanten Zugriff per ID, createSelector memoiziert teure Ableitungen und verhindert unnötige Rerenders.

Für Teams mit großem, geteiltem Client State und dem Bedürfnis nach erzwungenen Konventionen bleibt Redux Toolkit die robusteste Wahl unter den State Management Optionen für React, gerade weil DevTools, Middleware und TypeScript Integration bereits produktionsreif mitgeliefert werden. Kleinere Anwendungen mit überschaubarem Client State fahren dagegen oft leichter mit schlankeren Alternativen wie Zustand in Kombination mit TanStack Query.

Redux Toolkit: Das Wichtigste auf einen Blick

createSlice

Bündelt Action Types, Action Creators und Reducer. Dank Immer sind direkte Mutationen im Reducer Code erlaubt und trotzdem immutable.

RTK Query

Deklarative API Slices mit generierten Hooks, Tag basierter Cache Invalidation und automatischem Refetching, ersetzt manuelle Thunks.

Entity Adapter & Selektoren

createEntityAdapter normalisiert Listen für O(1) Zugriff, createSelector memoiziert teure Ableitungen.

TypeScript & DevTools

Typisierte Hooks über RootState und AppDispatch, DevTools mit Time Travel Debugging von Beginn an aktiviert.

11. FAQ: Redux Toolkit

1Ist Redux Toolkit dasselbe wie Redux?
Gleicher Store und gleiches Prinzip, aber ohne manuelle Boilerplate. createSlice, configureStore und createAsyncThunk ersetzen die klassische Schreibweise.
2Brauche ich RTK Query neben TanStack Query?
Beide lösen dasselbe Problem. Mit bestehendem Redux Store ist RTK Query naheliegender, ohne Redux Store oft TanStack Query die leichtere Wahl.
3Wie funktioniert Cache Invalidation in RTK Query?
Queries stellen Tags bereit, Mutationen invalidieren dieselben Tags, RTK Query triggert automatisch ein Refetch der betroffenen Queries.
4Warum bricht direktes Mutieren nicht Immutability?
Immer erzeugt hinter der scheinbaren Mutation ein neues unveränderliches State Objekt. Die Redux Grundregel bleibt vollständig erhalten.
5Wann createEntityAdapter einsetzen?
Bei Listen mit eindeutiger ID und häufigem Zugriff auf einzelne Elemente. Liefert O(1) Zugriff statt linearer Array Suche.
6Warum ist mein createSelector nicht memoiziert?
Meist gibt ein Input Selektor ein neues Objekt Literal zurück. Primitive Werte zurückgeben oder das Ergebnis selbst memoizieren.
7Brauche ich createAsyncThunk trotz RTK Query?
Für reines Fetching nicht mehr. Für mehrstufige Business Workflows und orchestrierte API Aufrufe bleibt es relevant.
8Wie typisiere ich useSelector korrekt?
RootState und AppDispatch ableiten, typisierte useAppSelector und useAppDispatch Hooks exportieren und überall verwenden.
9Was prüft die Serializable Check Middleware?
Sie warnt bei nicht serialisierbaren Werten wie Klassen Instanzen oder Promises im State, weil das Persistierung und DevTools Time Travel verhindern würde.
10Lohnt sich die Migration von Legacy Redux?
Meist ja, Slice für Slice migrierbar, Store bleibt unverändert. Boilerplate Rückgang und eingebaute DevTools rechtfertigen den Aufwand.