Skip to content

fix: modernize Redux extensions to RTK, re-enable type-check in CI#60

Merged
ulises-jeremias merged 1 commit into
mainfrom
fix/redux-thunk-and-deprecated-extensions
Apr 20, 2026
Merged

fix: modernize Redux extensions to RTK, re-enable type-check in CI#60
ulises-jeremias merged 1 commit into
mainfrom
fix/redux-thunk-and-deprecated-extensions

Conversation

@ulises-jeremias

@ulises-jeremias ulises-jeremias commented Apr 20, 2026

Copy link
Copy Markdown
Member

Summary

  • Modernize react-redux-thunk to use @reduxjs/toolkit (configureStore) instead of legacy createStore + applyMiddleware

    • Fixes CombinedState import error (type removed in Redux 5.x) — root cause of react-vite-boilerplate CI failures
    • Removes accidental saga imports (copy-paste bug from the saga extension)
    • Adds proper RootState / AppDispatch TypeScript types
    • Built-in DevTools support (no more window.__REDUX_DEVTOOLS_EXTENSION__)
  • Modernize react-redux-saga to use @reduxjs/toolkit + saga middleware

    • Same CombinedState and DevTools fixes
    • Saga middleware integrated via RTK's middleware option
  • Cleanup

    • Remove orphaned react-ionic/ directory (only contained .npmrc, not listed in templates.json)
    • Update Recoil.js description to note Meta no longer maintains it
    • Update templates.json descriptions for both Redux extensions
  • Re-enable type-check in CI — removes || true workaround that was masking real TypeScript errors

Testing

CI will run the random template+extension combinations. The react-vite-boilerplate + react-redux-thunk combo that was previously failing should now pass.

Summary by CodeRabbit

  • Bug Fixes

    • Workflow now properly fails when type-checking encounters errors instead of continuing.
  • New Features

    • Redux state management extensions now use Redux Toolkit with improved type safety and modern APIs.
  • Updates

    • Redux Saga and Redux Thunk extensions refactored to use Redux Toolkit's configureStore and async handling patterns.
    • Updated template descriptions and reordered state management options.

- Migrate react-redux-thunk to @reduxjs/toolkit (configureStore)
  - Remove CombinedState import (removed in Redux 5.x)
  - Remove saga imports (copy-paste bug from saga extension)
  - Remove manual DevTools setup (RTK handles it)
  - Add proper RootState/AppDispatch types

- Migrate react-redux-saga to @reduxjs/toolkit + saga middleware
  - Same CombinedState and DevTools fixes
  - Keep saga middleware via RTK middleware config

- Remove orphaned react-ionic directory (only had .npmrc, not in templates.json)
- Update Recoil.js description to note Meta no longer maintains it
- Update templates.json descriptions for Redux extensions
- Re-enable type-check in CI (remove || true workaround)
@coderabbitai

coderabbitai Bot commented Apr 20, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR migrates two Redux extension templates (react-redux-saga and react-redux-thunk) from Redux to Redux Toolkit, replacing manual store setup with configureStore, updating type definitions, removing Redux DevTools manual wiring, and refactoring dependencies. Additionally, CI workflow changes enforce type-check failures, npm config is simplified, and template metadata is updated.

Changes

Cohort / File(s) Summary
CI Workflow
.github/workflows/test-combinations.yml
Removed || true fallback from type-check step, making type-check failures now fail the job instead of continuing.
NPM Configuration
extensions/react-ionic/.npmrc
Removed legacy-peer-deps=true configuration.
React Redux Saga Reducer
extensions/react-redux-saga/[src]/reducers/index.ts
Changed from factory function () => combineReducers({...}) to constant rootReducer; switched combineReducers import from redux to @reduxjs/toolkit.
React Redux Saga Store
extensions/react-redux-saga/[src]/store.ts.template
Replaced Redux createStore/applyMiddleware/compose with Redux Toolkit configureStore; removed manual Redux DevTools wiring; added exported types RootState and AppDispatch; simplified middleware and persistence configuration.
React Redux Saga Types
extensions/react-redux-saga/[src]/types/global.d.ts, extensions/react-redux-saga/[src]/types/store.ts
Removed Redux DevTools window augmentation; replaced StoreType with store-derived RootState and AppDispatch types.
React Redux Saga Dependencies
extensions/react-redux-saga/package.json
Removed redux (^5.0.1), added @reduxjs/toolkit (^2.5.0), bumped react-redux to ^9.2.0, removed @types/react-redux.
React Redux Thunk README
extensions/react-redux-thunk/README.md
Updated documentation from Redux Thunk to Redux Toolkit approach; refactored examples to use createAsyncThunk and createSlice with typed selectors/dispatch.
React Redux Thunk Reducer
extensions/react-redux-thunk/[src]/reducers/index.ts
Changed from factory function to constant rootReducer; switched combineReducers import from redux to @reduxjs/toolkit.
React Redux Thunk Store
extensions/react-redux-thunk/[src]/store.ts.template
Replaced Redux setup with Redux Toolkit configureStore; removed saga middleware; updated persistence and middleware configuration; added exported types RootState and AppDispatch.
React Redux Thunk Types
extensions/react-redux-thunk/[src]/types/global.d.ts, extensions/react-redux-thunk/[src]/types/store.ts
Removed Redux DevTools window augmentation; replaced StoreType with store-derived RootState and AppDispatch types.
React Redux Thunk Dependencies
extensions/react-redux-thunk/package.json
Removed redux and redux-thunk, added @reduxjs/toolkit (^2.5.0), bumped react-redux to ^9.2.0, removed @types/react-redux.
Template Metadata
templates.json
Reordered state management templates (Jotai and Recoil swapped positions); updated react-redux-saga and react-redux-thunk descriptions to reflect Redux Toolkit adoption with consolidated tooling references.

Possibly related PRs

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Redux was old, now Toolkit's our friend,
No more manual wiring, the setup's more zen,
Reducers are constants, types now derive,
Persistence and middleware, all clean and alive! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main changes: modernizing Redux extensions to RTK and re-enabling type-check in CI, both of which are the primary objectives reflected in the file changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/redux-thunk-and-deprecated-extensions

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@extensions/react-redux-saga/`[src]/store.ts.template:
- Around line 30-32: The DEV-only logger isn't being registered because
middlewares.concat(createLogger() as any) returns a new array that is discarded;
update the DEV block to actually add the logger to the middleware array (e.g.,
reassign middlewares = middlewares.concat(createLogger()) or use
middlewares.push(createLogger())) so the logger middleware created by
createLogger() is included, and remove the unnecessary "as any" cast so the
logger uses the proper Middleware type; apply this change around the existing
middlewares variable and the createLogger() call in store.ts.template.

In `@extensions/react-redux-thunk/`[src]/store.ts.template:
- Around line 22-28: The DEV branch discards the result of
middlewares.concat(...) so redux-logger never gets added; update the logic in
the function that builds middlewares so the logger is actually appended (e.g.,
assign the concat result back to middlewares or use push with createLogger() as
any) when import.meta.env.DEV is true, and if necessary adjust types by removing
an overly strict explicit annotation or importing ConfigureStoreOptions from
`@reduxjs/toolkit` so the middleware array type accepts the logger (references:
middlewares, createLogger(), import.meta.env.DEV, concat).
- Line 16: The middleware callback is typed incorrectly: using ReturnType<typeof
configureStore>['middleware'] breaks RTK inference for getDefaultMiddleware and
serializable/dispatch types; change the signature for the middleware factory to
accept CurriedGetDefaultMiddleware<RootState> (or simply use an untyped inline
parameter middleware: (getDefaultMiddleware) => { ... } inside configureStore to
let TS infer) and remove the unnecessary "as any" cast on createLogger(); update
references in this file to use CurriedGetDefaultMiddleware<RootState> (or the
inline middleware callback) and keep configureStore and createLogger usage
unchanged otherwise.

In `@extensions/react-redux-thunk/README.md`:
- Around line 33-35: The README example references useEffect but the top import
block only imports react-redux and Redux Toolkit symbols; update the imports to
include useEffect from React (e.g., add an import for useEffect from 'react'
alongside the existing imports) so the example that uses useEffect compiles;
locate the import block containing useSelector/useDispatch and add the useEffect
symbol to the imports.
- Around line 43-60: Add the missing slice registration so the example works:
export the slice reducer from userSlice (e.g., userSlice.reducer) and include it
when building the root reducer (via combineReducers) and store (e.g.,
configureStore/createStore) so RootState has a user field; then update the
example to import the store/provider or RootState type accordingly so
useSelector((state: RootState) => state.user) resolves at runtime.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1f811627-b7e7-4d94-85f7-899c281ee053

📥 Commits

Reviewing files that changed from the base of the PR and between b83a56b and d3ba2f5.

📒 Files selected for processing (14)
  • .github/workflows/test-combinations.yml
  • extensions/react-ionic/.npmrc
  • extensions/react-redux-saga/[src]/reducers/index.ts
  • extensions/react-redux-saga/[src]/store.ts.template
  • extensions/react-redux-saga/[src]/types/global.d.ts
  • extensions/react-redux-saga/[src]/types/store.ts
  • extensions/react-redux-saga/package.json
  • extensions/react-redux-thunk/README.md
  • extensions/react-redux-thunk/[src]/reducers/index.ts
  • extensions/react-redux-thunk/[src]/store.ts.template
  • extensions/react-redux-thunk/[src]/types/global.d.ts
  • extensions/react-redux-thunk/[src]/types/store.ts
  • extensions/react-redux-thunk/package.json
  • templates.json
💤 Files with no reviewable changes (3)
  • extensions/react-ionic/.npmrc
  • extensions/react-redux-saga/[src]/types/global.d.ts
  • extensions/react-redux-thunk/[src]/types/global.d.ts

Comment on lines +30 to +32
if (import.meta.env.DEV) {
middlewares.concat(createLogger() as any);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Bug: redux-logger is never attached in DEV — same .concat issue as the thunk template.

middlewares.concat(createLogger() as any) returns a new array and discards it, so the logger middleware is built but never registered with the store. Only the saga middleware (attached via the reassigned .concat on line 28) actually runs.

🔧 Proposed fix
-    const middlewares = getDefaultMiddleware({
+    let middlewares = getDefaultMiddleware({
       thunk: false,
       serializableCheck: {
         ignoredActions: ['persist/PERSIST', 'persist/REHYDRATE'],
       },
     }).concat(sagaMiddleware);

     if (import.meta.env.DEV) {
-      middlewares.concat(createLogger() as any);
+      middlewares = middlewares.concat(createLogger());
     }

     return middlewares;

Also consider dropping as anyredux-logger provides compatible Middleware types, and keeping any here undermines the type-check you're re-enabling in CI.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@extensions/react-redux-saga/`[src]/store.ts.template around lines 30 - 32,
The DEV-only logger isn't being registered because
middlewares.concat(createLogger() as any) returns a new array that is discarded;
update the DEV block to actually add the logger to the middleware array (e.g.,
reassign middlewares = middlewares.concat(createLogger()) or use
middlewares.push(createLogger())) so the logger middleware created by
createLogger() is included, and remove the unnecessary "as any" cast so the
logger uses the proper Middleware type; apply this change around the existing
middlewares variable and the createLogger() call in store.ts.template.


// Push middleware that you need for both development and production
packages.push(sagaMiddleware);
const middleware = (getDefaultMiddleware: ReturnType<typeof configureStore>['middleware']) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In @reduxjs/toolkit2.5, what is the correct TypeScript type for thegetDefaultMiddlewarecallback parameter passed toconfigureStore's middleware option?

💡 Result:

The correct TypeScript type for the getDefaultMiddleware callback parameter passed to configureStore's middleware option in @reduxjs/toolkit 2.5 is GetDefaultMiddleware, where S is the store state type.

Citations:


🏁 Script executed:

# First, locate the file in question
fd -p "store.ts.template" extensions/

Repository: Create-Node-App/cna-templates

Length of output: 177


🏁 Script executed:

# Check the actual content of the file
if [ -f "extensions/react-redux-thunk/src/store.ts.template" ]; then
  cat -n "extensions/react-redux-thunk/src/store.ts.template"
fi

Repository: Create-Node-App/cna-templates

Length of output: 55


🏁 Script executed:

# Search for any Redux Toolkit type definitions or imports in the codebase to understand what types are available
rg "GetDefaultMiddleware|CurriedGetDefaultMiddleware|ConfigureStoreOptions" -A 2 -B 2

Repository: Create-Node-App/cna-templates

Length of output: 55


🏁 Script executed:

# Check if there's a node_modules or package.json to understand which version of RTK is used
if [ -f "package.json" ]; then
  cat package.json | grep -A 5 "@reduxjs/toolkit"
fi

Repository: Create-Node-App/cna-templates

Length of output: 55


🏁 Script executed:

# Also check for any type definitions or tsconfig to understand the TS setup
find . -maxdepth 2 -name "tsconfig.json" -o -name "tsconfig*.json"

Repository: Create-Node-App/cna-templates

Length of output: 55


🏁 Script executed:

# Read the actual file content with line numbers
cat -n extensions/react-redux-thunk/src/store.ts.template

Repository: Create-Node-App/cna-templates

Length of output: 155


🏁 Script executed:

# Find package.json files
find . -name "package.json" -type f

Repository: Create-Node-App/cna-templates

Length of output: 1903


🏁 Script executed:

# Check for redux-logger type definitions and configuration
rg "redux-logger|createLogger" -A 3 -B 1

Repository: Create-Node-App/cna-templates

Length of output: 4042


🏁 Script executed:

# Search for type imports or definitions related to middleware configuration
rg "middleware|GetDefaultMiddleware|CurriedGetDefaultMiddleware" extensions/react-redux-thunk/ -A 2

Repository: Create-Node-App/cna-templates

Length of output: 1407


🏁 Script executed:

# Search for Redux Toolkit type definitions to verify the correct types
web_search

Repository: Create-Node-App/cna-templates

Length of output: 123


🌐 Web query:

Redux Toolkit ConfigureStoreOptions middleware type definition CurriedGetDefaultMiddleware

💡 Result:

The CurriedGetDefaultMiddleware type is used in Redux Toolkit's ConfigureStoreOptions interface to define the middleware callback parameter. It represents a curried function (getDefaultMiddleware: CurriedGetDefaultMiddleware) => M where CurriedGetDefaultMiddleware is the type of the getDefaultMiddleware function passed to the middleware callback. This function has the signature that returns a Tuple<Middlewares> (a type-safe array of Middleware<{}, S>[]), which can be chained with .concat or .prepend methods. It is built internally using buildGetDefaultMiddleware from ./getDefaultMiddleware. Key type definitions from source: - export type Middlewares = ReadonlyArray<Middleware<{}, S>> - middleware?: (getDefaultMiddleware: GetDefaultMiddleware) => M (older naming, aliased/equivalent to CurriedGetDefaultMiddleware) - Full interface: ConfigureStoreOptions<S = any, A extends Action = UnknownAction, M extends Tuple<Middlewares> = Tuple<Middlewares>, E extends Tuple = Tuple, P = S> Usage example: middleware: (getDefaultMiddleware: CurriedGetDefaultMiddleware) => getDefaultMiddleware.concat(customMiddleware) This ensures TypeScript inference for the store's dispatch type based on the middleware tuple returned.

Citations:


🏁 Script executed:

# Check the actual Redux Toolkit types available for import
rg "ConfigureStoreOptions|CurriedGetDefaultMiddleware|GetDefaultMiddleware" -t ts -t tsx

Repository: Create-Node-App/cna-templates

Length of output: 100


🏁 Script executed:

# Look at the complete store.ts.template file to understand context
cat -n "extensions/react-redux-thunk/[src]/store.ts.template"

Repository: Create-Node-App/cna-templates

Length of output: 1458


Incorrect type annotation for getDefaultMiddleware — breaks type inference for the middleware callback.

ReturnType<typeof configureStore>['middleware'] does not resolve to the callback signature RTK expects. The EnhancedStore type does not expose a public middleware property with that shape, causing type loss for serializableCheck and downstream dispatch typing.

Use CurriedGetDefaultMiddleware<RootState> directly, or inline the callback: configureStore({ middleware: (getDefaultMiddleware) => { ... } }) to let TypeScript infer the type.

Also remove the unnecessary as any cast on createLogger()redux-logger (3.0.6) with @types/redux-logger (3.0.13) ships proper types.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@extensions/react-redux-thunk/`[src]/store.ts.template at line 16, The
middleware callback is typed incorrectly: using ReturnType<typeof
configureStore>['middleware'] breaks RTK inference for getDefaultMiddleware and
serializable/dispatch types; change the signature for the middleware factory to
accept CurriedGetDefaultMiddleware<RootState> (or simply use an untyped inline
parameter middleware: (getDefaultMiddleware) => { ... } inside configureStore to
let TS infer) and remove the unnecessary "as any" cast on createLogger(); update
references in this file to use CurriedGetDefaultMiddleware<RootState> (or the
inline middleware callback) and keep configureStore and createLogger usage
unchanged otherwise.

Comment on lines 22 to +28

if (import.meta.env.DEV) {
// Push the middleware that are specific for development
packages.push(createLogger());
if (window.__REDUX_DEVTOOLS_EXTENSION__) {
enhancers.push(window.__REDUX_DEVTOOLS_EXTENSION__());
if (import.meta.env.DEV) {
middlewares.concat(createLogger() as any);
}
}

const middleware = applyMiddleware(...packages);

export const store = createStore(
persistReducer<StoreType>(persistConfig, reducers()),
compose(middleware, ...enhancers),
);
return middlewares;
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Bug: redux-logger is never attached in DEV — .concat result is discarded.

Array#concat (and RTK's MiddlewareArray#concat) returns a new array; it doesn't mutate in place. So in DEV the logger middleware is created but thrown away, and the returned middlewares is identical to the base one. This is a regression from the previous saga-based wiring.

🔧 Proposed fix
-const middleware = (getDefaultMiddleware: ReturnType<typeof configureStore>['middleware']) => {
-  const middlewares = getDefaultMiddleware({
-    serializableCheck: {
-      ignoredActions: ['persist/PERSIST', 'persist/REHYDRATE'],
-    },
-  });
-
-  if (import.meta.env.DEV) {
-    middlewares.concat(createLogger() as any);
-  }
-
-  return middlewares;
-};
+const middleware: ConfigureStoreOptions['middleware'] = (getDefaultMiddleware) => {
+  const base = getDefaultMiddleware({
+    serializableCheck: {
+      ignoredActions: ['persist/PERSIST', 'persist/REHYDRATE'],
+    },
+  });
+
+  return import.meta.env.DEV ? base.concat(createLogger()) : base;
+};

(Adjust the import to include ConfigureStoreOptions from @reduxjs/toolkit, or drop the explicit annotation and inline the callback so TS infers types from configureStore.)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@extensions/react-redux-thunk/`[src]/store.ts.template around lines 22 - 28,
The DEV branch discards the result of middlewares.concat(...) so redux-logger
never gets added; update the logic in the function that builds middlewares so
the logger is actually appended (e.g., assign the concat result back to
middlewares or use push with createLogger() as any) when import.meta.env.DEV is
true, and if necessary adjust types by removing an overly strict explicit
annotation or importing ConfigureStoreOptions from `@reduxjs/toolkit` so the
middleware array type accepts the logger (references: middlewares,
createLogger(), import.meta.env.DEV, concat).

Comment on lines 33 to +35
import { useSelector, useDispatch } from 'react-redux';

// Thunk action creator
const fetchUser = (userId) => async (dispatch) => {
dispatch({ type: 'FETCH_USER_START' });
try {
const user = await api.getUser(userId);
dispatch({ type: 'FETCH_USER_SUCCESS', payload: user });
} catch (error) {
dispatch({ type: 'FETCH_USER_ERROR', payload: error.message });
}
};

function UserProfile({ userId }) {
const user = useSelector(state => state.user);
const dispatch = useDispatch();
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import type { RootState, AppDispatch } from './store';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -name README.md -path "*/react-redux-thunk/*" | head -5

Repository: Create-Node-App/cna-templates

Length of output: 113


🏁 Script executed:

wc -l extensions/react-redux-thunk/README.md

Repository: Create-Node-App/cna-templates

Length of output: 114


🏁 Script executed:

cat -n extensions/react-redux-thunk/README.md | sed -n '30,70p'

Repository: Create-Node-App/cna-templates

Length of output: 1442


Import useEffect in the README example.

Line 63 uses useEffect, but the import statements at lines 33–35 only include Redux hooks, so a copy-paste example fails to compile.

📝 Proposed README fix
+import { useEffect } from 'react';
 import { useSelector, useDispatch } from 'react-redux';
 import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
 import type { RootState, AppDispatch } from './store';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { useSelector, useDispatch } from 'react-redux';
// Thunk action creator
const fetchUser = (userId) => async (dispatch) => {
dispatch({ type: 'FETCH_USER_START' });
try {
const user = await api.getUser(userId);
dispatch({ type: 'FETCH_USER_SUCCESS', payload: user });
} catch (error) {
dispatch({ type: 'FETCH_USER_ERROR', payload: error.message });
}
};
function UserProfile({ userId }) {
const user = useSelector(state => state.user);
const dispatch = useDispatch();
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import type { RootState, AppDispatch } from './store';
import { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import type { RootState, AppDispatch } from './store';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@extensions/react-redux-thunk/README.md` around lines 33 - 35, The README
example references useEffect but the top import block only imports react-redux
and Redux Toolkit symbols; update the imports to include useEffect from React
(e.g., add an import for useEffect from 'react' alongside the existing imports)
so the example that uses useEffect compiles; locate the import block containing
useSelector/useDispatch and add the useEffect symbol to the imports.

Comment on lines +43 to +60
// Create a slice
const userSlice = createSlice({
name: 'user',
initialState: { data: null, loading: false },
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => { state.loading = true; })
.addCase(fetchUser.fulfilled, (state, action) => {
state.loading = false;
state.data = action.payload;
});
},
});

// Use in components
function UserProfile({ userId }: { userId: string }) {
const user = useSelector((state: RootState) => state.user);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -name "README.md" -path "*/react-redux-thunk/*" | head -20

Repository: Create-Node-App/cna-templates

Length of output: 113


🏁 Script executed:

cat -n ./extensions/react-redux-thunk/README.md | sed -n '40,70p'

Repository: Create-Node-App/cna-templates

Length of output: 1023


🏁 Script executed:

cat -n ./extensions/react-redux-thunk/README.md | head -50

Repository: Create-Node-App/cna-templates

Length of output: 2129


🏁 Script executed:

cat -n ./extensions/react-redux-thunk/README.md | sed -n '60,100p'

Repository: Create-Node-App/cna-templates

Length of output: 630


🏁 Script executed:

cat -n ./extensions/react-redux-thunk/README.md

Repository: Create-Node-App/cna-templates

Length of output: 2969


Add slice registration step to complete the example.

The example creates userSlice and immediately uses state.user in the component, but doesn't show how to register the reducer. Without adding the slice to the root reducer via combineReducers, the selector would fail at runtime. Include the registration step so the example is complete and functional.

Suggested addition after the slice definition
 const userSlice = createSlice({
   name: 'user',
   initialState: { data: null, loading: false },
   reducers: {},
   extraReducers: (builder) => {
     builder
       .addCase(fetchUser.pending, (state) => { state.loading = true; })
       .addCase(fetchUser.fulfilled, (state, action) => {
         state.loading = false;
         state.data = action.payload;
       });
   },
 });
+
+// Register in your root reducer:
+// const rootReducer = combineReducers({
+//   user: userSlice.reducer,
+// });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Create a slice
const userSlice = createSlice({
name: 'user',
initialState: { data: null, loading: false },
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => { state.loading = true; })
.addCase(fetchUser.fulfilled, (state, action) => {
state.loading = false;
state.data = action.payload;
});
},
});
// Use in components
function UserProfile({ userId }: { userId: string }) {
const user = useSelector((state: RootState) => state.user);
// Create a slice
const userSlice = createSlice({
name: 'user',
initialState: { data: null, loading: false },
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => { state.loading = true; })
.addCase(fetchUser.fulfilled, (state, action) => {
state.loading = false;
state.data = action.payload;
});
},
});
// Register in your root reducer:
// const rootReducer = combineReducers({
// user: userSlice.reducer,
// });
// Use in components
function UserProfile({ userId }: { userId: string }) {
const user = useSelector((state: RootState) => state.user);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@extensions/react-redux-thunk/README.md` around lines 43 - 60, Add the missing
slice registration so the example works: export the slice reducer from userSlice
(e.g., userSlice.reducer) and include it when building the root reducer (via
combineReducers) and store (e.g., configureStore/createStore) so RootState has a
user field; then update the example to import the store/provider or RootState
type accordingly so useSelector((state: RootState) => state.user) resolves at
runtime.

@ulises-jeremias ulises-jeremias merged commit f144099 into main Apr 20, 2026
14 of 16 checks passed
ulises-jeremias added a commit that referenced this pull request Apr 20, 2026
Remove react-easy-state (@risingstack/react-easy-state, abandoned
since 2022) and react-global-state (react-hooks-global-state,
abandoned since 2022) extensions entirely.

Mark Teaful as deprecated in templates.json — only ~126 weekly
npm downloads and pre-1.0 stability. Recoil was already marked
deprecated in PR #60.

Hookstate (74k downloads/week) and Million (31k downloads/week)
are kept as healthy extensions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant