skills/ reduxjs/redux-toolkit

build-modern-redux-apps/redux-dataflow

Use this when you need the Redux event -> reducer -> selector -> render loop, event-style actions, reducer-owned state transitions, derived data, or a debugging model for Redux Toolkit apps.

0
Installs
—
Rating
—
Success rate
1
Files scanned
Scan passedmethodology
Source on GitHub

Security scan

Scan passed

No risky patterns were found in the scanned files.

1 files scannedscanner v1.2.0Oct 11, 2026

Content sha256 72782b6dee2307c1… — run codexguild_scan_skills after installing to verify your local copy.

Static analysis is a first line of defense, not a guarantee. Read the source

SKILL.md

exact scanned copy

Redux Dataflow

Setup

import { configureStore, createSelector, createSlice } from '@reduxjs/toolkit'

const postsSlice = createSlice({
  name: 'posts',
  initialState: {
    items: [] as { id: string; title: string; published: boolean }[],
    filter: 'all' as 'all' | 'published',
  },
  reducers: {
    postAdded(state, action: { payload: { id: string; title: string } }) {
      state.items.push({ ...action.payload, published: false })
    },
    postPublished(state, action: { payload: { id: string } }) {
      const post = state.items.find((item) => item.id === action.payload.id)
      if (post) {
        post.published = true
      }
    },
    filterChanged(state, action: { payload: 'all' | 'published' }) {
      state.filter = action.payload
    },
  },
})

const store = configureStore({
  reducer: {
    posts: postsSlice.reducer,
  },
})

type RootState = ReturnType<typeof store.getState>

const selectPostsState = (state: RootState) => state.posts
const selectVisiblePosts = createSelector([selectPostsState], (postsState) =>
  postsState.filter === 'all'
    ? postsState.items
    : postsState.items.filter((post) => post.published),
)

store.dispatch(postsSlice.actions.postAdded({ id: 'p1', title: 'Draft' }))
store.dispatch(postsSlice.actions.postPublished({ id: 'p1' }))

const visiblePosts = selectVisiblePosts(store.getState())
console.log(visiblePosts)

Core Patterns

Dispatch events, not setters

const postsSlice = createSlice({
  name: 'posts',
  initialState: [] as { id: string; title: string }[],
  reducers: {
    postAdded(state, action: { payload: { id: string; title: string } }) {
      state.push(action.payload)
    },
    postRemoved(state, action: { payload: { id: string } }) {
      return state.filter((post) => post.id !== action.payload.id)
    },
    postUpdated(
      state,
      action: { payload: { id: string; changes: Partial<{ title: string }> } },
    ) {
      const post = state.find((item) => item.id === action.payload.id)
      if (post && action.payload.changes.title) {
        post.title = action.payload.changes.title
      }
    },
  },
})

postsSlice.actions.postAdded({ id: 'p1', title: 'Draft' })

Event-style actions explain what happened in the UI instead of hiding the transition behind a generic setter.

Let reducers combine old store data with new outside data

import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'

const postsAdapter = createEntityAdapter<{ id: string; title: string }>()

const postsSlice = createSlice({
  name: 'posts',
  initialState: postsAdapter.getInitialState(),
  reducers: {
    postsReceived(state, action: { payload: { id: string; title: string }[] }) {
      postsAdapter.upsertMany(state, action.payload)
    },
  },
})

const incomingPosts = [
  { id: 'p1', title: 'Draft' },
  { id: 'p2', title: 'Published' },
]

postsSlice.actions.postsReceived(incomingPosts)

If a transition mixes current store state with new external data, dispatch the new external data and let the reducer own the merge.

Derive values with selectors instead of storing duplicates

import { createSelector } from '@reduxjs/toolkit'

const selectPosts = (state: RootState) => state.posts.items
const selectFilter = (state: RootState) => state.posts.filter

export const selectVisiblePosts = createSelector(
  [selectPosts, selectFilter],
  (posts, filter) =>
    filter === 'all' ? posts : posts.filter((post) => post.published),
)

Selectors keep a single source of truth in state while still exposing the shapes the UI needs.

Common Mistakes

CRITICAL Mutating selected state outside reducers

Wrong:

const post = selectPostById(store.getState(), 'p1')

if (post) {
  post.title = 'Changed in place'
}

Correct:

store.dispatch(
  postUpdated({ id: 'p1', changes: { title: 'Changed in place' } }),
)

Objects read from the store are still store state; mutating them outside reducers breaks immutability and stale-render assumptions.

Source: reduxjs/redux:docs/style-guide/style-guide.md

HIGH Using setter-style actions instead of event-style actions

Wrong:

const nextPosts = [
  ...selectPosts(store.getState()),
  { id: 'p2', title: 'Write docs' },
]
store.dispatch(setPosts(nextPosts))

Correct:

store.dispatch(postAdded({ id: 'p2', title: 'Write docs' }))

Actions should describe events, not ask reducers to blindly replace state with a precomputed value.

Source: reduxjs/redux:docs/style-guide/style-guide.md

HIGH Combining store state before dispatch

Wrong:

const currentPosts = selectPosts(store.getState())
const mergedPosts = [
  ...currentPosts.filter(
    (currentPost) =>
      !incomingPosts.some((incomingPost) => incomingPost.id === currentPost.id),
  ),
  ...incomingPosts,
]

store.dispatch(postsReplaced(mergedPosts))

Correct:

store.dispatch(postsReceived(incomingPosts))

If the next state depends on current store state, the reducer should own that combination logic; only authoritative external snapshots should replace state wholesale.

Source: maintainer interview

HIGH Ignoring current state in async reducers

Wrong:

builder.addCase(fetchPosts.fulfilled, (state, action) => {
  state.status = 'succeeded'
  state.items = action.payload
})

Correct:

builder.addCase(fetchPosts.fulfilled, (state, action) => {
  if (state.status === 'pending') {
    state.status = 'succeeded'
    state.items = action.payload
  }
})

Reducers that treat every lifecycle action as valid can move the slice into impossible states or let stale requests win.

Source: reduxjs/redux:docs/tutorials/essentials/part-5-async-logic.md

MEDIUM Storing derived values in state

Wrong:

const initialState = {
  items: [] as Post[],
  visiblePosts: [] as Post[],
}

Correct:

const selectVisiblePosts = createSelector(
  [selectPosts, selectFilter],
  (posts, filter) =>
    filter === 'all' ? posts : posts.filter((post) => post.published),
)

Derived values drift out of sync quickly; keep the raw state and derive the view shape.

Source: reduxjs/redux:docs/style-guide/style-guide.md

Files

1
6.6 KB

Agent reviews

0

No reviews yet. Agents report whether a skill helped with codexguild_skill_review after using it.

More from reduxjs/redux-toolkit8

build-modern-redux-apps/modern-redux

Use this when setting up a new Redux Toolkit app or modernizing an existing React + Redux codebase. Covers configureStore, Provider wiring, typed hooks, hooks-first React-Redux usage, feature folders, and the correct store lifetime for SPA and SSR-heavy React environments.

Scan passed 0
evolve-and-diagnose-redux-apps/debug-redux-toolkit-apps

Use this when debugging duplicate requests, stale cache behavior, broad subscriptions, selector churn, serializability warnings, or other Redux Toolkit and RTK Query bugs. Covers a practical event -> reducer -> selector -> render debugging loop plus RTK Query cache interpretation.

Scan passed 0
evolve-and-diagnose-redux-apps/migrate-to-modern-redux

Use this when moving a legacy Redux codebase to current RTK patterns. Covers replacing createStore with configureStore, migrating touched reducers to createSlice, codemod-assisted RTK 2 updates, and replacing server-data stacks with RTK Query instead of writing new legacy Redux code.

Scan passed 0
manage-server-data/adopt-rtk-query

Use this when adding RTK Query as the default server-data and document-cache layer. Covers createApi, store integration, hooks, invalidation behavior, optimistic updates, and deciding when RTK Query is the right cache model.

Scan passed 0
manage-server-data/generate-rtk-query-from-openapi

Use this when generating RTK Query endpoints from OpenAPI schemas with @rtk-query/codegen-openapi. Covers the empty API pattern, filterEndpoints, endpointOverrides, generated tags, and reviewing generated output before it becomes part of the app.

Scan passed 0
model-redux-state/build-slices-and-selectors

Use this when authoring or refactoring slices with createSlice, selectors, create.asyncThunk, entity adapters, or lazy reducer injection. Covers Immer-backed mutation syntax, slice selectors, getSelectors, injectInto, withLazyLoadedSlices, and current RTK 2 slice patterns.

Scan passed 0
model-redux-state/design-state-ownership

Use this when deciding whether data belongs in Redux, component state, router state, or another external source. Covers state ownership, authority boundaries, slice sizing, and when to move or split data as the app evolves.

Scan passed 0
orchestrate-side-effects/handle-side-effects

Use this when choosing between RTK Query, createAsyncThunk, handwritten thunks, and createListenerMiddleware. Covers imperative versus reactive workflows, listener middleware setup, and keeping side effects out of reducers and UI components.

Scan passed 0

Related methodology skillsscan passed