Redux-Bundler Create React App Template
cra-template-redux-bundler provides a starting point for building React applications using Create React App with redux-bundler integrated. Redux-bundler, currently at version 29.1.0, is a state management library designed to simplify Redux applications by organizing logic into modular "bundles." It promotes a feature-centric architecture, moving away from the traditional Redux separation of actions, reducers, and selectors into separate directories. Key features include a "batteries included" philosophy with optional routing and data fetching capabilities, a "reactor" pattern for declarative state reactions, and built-in support for code-splitting and lazy-loading of state logic. This approach significantly reduces boilerplate and aims to improve maintainability and scalability for complex applications, particularly Progressive Web Apps (PWAs) that benefit from small bundle sizes and network resilience. The template itself is at version 1.0.0 and streamlines the initial setup process, configuring the necessary redux-bundler infrastructure within a standard Create React App project.
Common errors
-
Cannot read properties of undefined (reading 'selectSomeValue')
cause A selector was referenced in `connect()` or a reactor, but the corresponding bundle was not composed into the store, or the selector name does not follow the `select` prefix convention.fixVerify that all bundles containing the required selectors are passed to `composeBundles` and that selectors are correctly named (e.g., `selectMyData`). -
TypeError: store.doSomething is not a function
cause An action creator (`doSomething`) defined in a bundle was called directly on the store object, but it was not properly bound or the bundle was not integrated correctly.fixEnsure the bundle defining `doSomething` is included in `composeBundles`. When using `redux-bundler-react.connect`, list the action creator name (e.g., `'doSomething'`) in the arguments to receive it as a prop. If using hooks, ensure `useBundler` correctly extracts it. -
Error: `createStore` expects the first argument to be a reducer
cause When using `redux-bundler`, `createStore` expects a store enhancer (the result of `composeBundles`), not a traditional Redux reducer.fixPass the output of `composeBundles(yourBundle1, yourBundle2, ...)` directly to `createStore`.
Warnings
- deprecated The underlying Create React App (CRA) project is now in maintenance mode and no longer actively developed by Meta. While it remains functional, new projects may consider alternative build tools like Vite, Next.js, or similar, which offer faster development and more modern features.
- gotcha Redux-bundler enforces specific naming conventions, particularly for selectors, which must start with the prefix 'select' (e.g., `selectUserName`). Violating this convention will prevent the bundler from recognizing and integrating the selector correctly.
- gotcha Redux-bundler bundles Redux and other dependencies internally. If `NODE_ENV` is not set to 'production' during your build process, Redux's debug blocks may be included in your production bundle, increasing its size.
- gotcha Redux-bundler streamlines Redux, but it still represents a learning curve for developers unfamiliar with Redux's core concepts (actions, reducers, middleware) and its own convention-over-configuration approach.
Install
-
npm install cra-template-redux-bundler -
yarn add cra-template-redux-bundler -
pnpm add cra-template-redux-bundler
Imports
- composeBundles
import composeBundles from 'redux-bundler'
import { composeBundles } from 'redux-bundler' - createStore
import { createStore } from 'redux'import { createStore } from 'redux-bundler' - connect
import { connect } from 'react-redux'import { connect } from 'redux-bundler-react' - useBundler
import { useBundler } from 'redux-bundler-hook'import useBundler from 'redux-bundler-hook'
Quickstart
import React from 'react';
import ReactDOM from 'react-dom/client';
import { createStore, composeBundles } from 'redux-bundler';
import { Provider } from 'react-redux'; // Often used with redux-bundler
import { connect } from 'redux-bundler-react';
const userBundle = {
name: 'user',
reducer: (state = { name: 'Guest', loggedIn: false }, action) => {
if (action.type === 'USER_LOGIN_SUCCESS') {
return { ...state, name: action.payload.name, loggedIn: true };
}
if (action.type === 'USER_LOGOUT') {
return { ...state, name: 'Guest', loggedIn: false };
}
return state;
},
selectUserName: (state) => state.user.name,
selectIsLoggedIn: (state) => state.user.loggedIn,
doLogin: (name) => ({ dispatch }) => {
// Simulate API call
setTimeout(() => {
dispatch({ type: 'USER_LOGIN_SUCCESS', payload: { name } });
}, 500);
},
doLogout: () => ({ dispatch }) => {
dispatch({ type: 'USER_LOGOUT' });
},
};
const appBundles = composeBundles(userBundle);
const store = createStore(appBundles);
function MyComponent({ userName, isLoggedIn, doLogin, doLogout }) {
return (
<div>
<h1>Hello, {userName}!</h1>
{isLoggedIn ? (
<button onClick={doLogout}>Logout</button>
) : (
<button onClick={() => doLogin('Alice')}>Login as Alice</button>
)}
</div>
);
}
const ConnectedMyComponent = connect(
'selectUserName',
'selectIsLoggedIn',
'doLogin',
'doLogout',
)(MyComponent);
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<ConnectedMyComponent />
</Provider>
);