Library to interact with redux toolkit and reduce boilerplate / repeated code
npm install @feardread/feature-factoryA comprehensive collection of factory utilities for Redux applications, providing standardized patterns for creating slices, async thunks, state management, and caching. This suite eliminates boilerplate code and enforces consistent patterns across your Redux application.
- Overview
- Installation
- Core Components
- Quick Start
- API Reference
- Examples
- Best Practices
- Contributing
The Redux Factory Suite consists of five main components:
1. FeatureFactory - Creates complete Redux features with slices, async actions, and reducers
2. ThunkFactory - Generates standardized async thunks for API operations
3. StateFactory - Creates comprehensive initial state structures
4. CacheFactory - Provides browser storage utilities with fallback support
5. ApiFactory - Creates RTK Query APIs with standardized CRUD operations and caching
``bash`
npm install @feardread/feature-factoryThe factory utilities are included in your project source
dependencies:
`bash`
npm install @reduxjs/toolkit react-redux
Creates complete Redux features with standardized structure including slices, async actions, and reducers.
`javascript
import { FeatureFactory } from './FeatureFactory';
const userFeature = FeatureFactory('users', {
setCurrentUser: (state, action) => {
state.currentUser = action.payload;
}
});
const { slice, asyncActions } = userFeature.create();
`
Factory for creating Redux async thunks with standardized patterns for HTTP operations.
`javascript
import ThunkFactory from './ThunkFactory';
// Create individual thunks
const fetchUsers = ThunkFactory.create('users', 'all');
const createUser = ThunkFactory.post('users', 'create');
// Create full CRUD suite
const userThunks = ThunkFactory.createCrud('users');
`
Creates comprehensive initial state structures with optional features like pagination, filtering, and validation.
`javascript
import StateFactory from './StateFactory';
// Full-featured state
const initialState = StateFactory('users');
// Minimal state
const minimalState = StateFactory.minimal('users');
// List-optimized state
const listState = StateFactory.forList('users');
`
Browser storage utilities with automatic fallback to memory storage.
`javascript
import CacheFactory from './CacheFactory';
const cache = CacheFactory({ type: 'local', prefix: 'myapp_' });
await cache.set('user', userData);
const user = await cache.get('user');
`
Creates RTK Query APIs with standardized CRUD operations, caching, and tag invalidation.
`javascript
import ApiFactory from './ApiFactory';
// Create complete API with standard CRUD endpoints
const usersApi = ApiFactory.createComplete('users');
// Create custom API
const customApi = ApiFactory('products')
.inject('getFeatured', {
method: 'GET',
url: 'products/featured',
providesTags: ['Product']
})
.create();
`
Here's how to create a complete feature using all components:
`javascript
import { FeatureFactory } from './FeatureFactory';
import StateFactory from './StateFactory';
import ThunkFactory from './ThunkFactory';
import ApiFactory from './ApiFactory';
// 1. Create RTK Query API (recommended for new projects)
const productsApi = ApiFactory.createComplete('products', {
tagTypes: ['Product', 'Category']
});
// OR use traditional async thunks
// 1. Create initial state
const initialState = StateFactory.forList('products');
// 2. Create custom async actions
const customActions = {
searchProducts: ThunkFactory.create('products', 'search'),
getPopularProducts: ThunkFactory.custom('products', 'popular', {
method: 'GET',
customUrl: 'products/popular'
})
};
// 3. Create the feature
const productFeature = FeatureFactory('products', {
toggleFavorite: (state, action) => {
const product = state.products.find(p => p.id === action.payload);
if (product) {
product.isFavorite = !product.isFavorite;
}
}
});
// 4. Generate slice and actions
const { slice, asyncActions } = productFeature.create({
service: customActions,
initialState
});
export const productReducer = slice.reducer;
export const productActions = { ...slice.actions, ...asyncActions };
// For RTK Query approach, use generated hooks:
// const { data, error, isLoading } = productsApi.useGetAllQuery();
`
#### FeatureFactory(entity, reducers, endpoints)
Creates a feature factory instance.
Parameters:
- entity (string): Entity name for the featurereducers
- (Object): Custom reducers to include in the sliceendpoints
- (Object|null): API endpoints (currently unused)
Returns: Feature factory instance
#### Methods
- create(options): Creates slice and async actionsmanager(initialReducers)
- : Creates dynamic reducer managerinject(source, destination)
- : Merges objectsgetEntityName()
- : Returns entity namegetAdapter()
- : Returns RTK entity adapter
#### Static Methods
- create(entity, prefix): Creates GET request thunkpost(entity, prefix)
- : Creates POST request thunk put(entity, prefix)
- : Creates PUT request thunkpatch(entity, prefix)
- : Creates PATCH request thunkdelete(entity, prefix)
- : Creates DELETE request thunkcustom(entity, prefix, options)
- : Creates custom configured thunkcreateCrud(entity)
- : Creates complete CRUD thunk suite
#### Standard Operations
- all: GET /entity/allone
- : GET /entity/:id search
- : GET /entity/search?paramscreate
- : POST /entity/createupdate
- : PUT /entity/:idpatch
- : PATCH /entity/:iddelete
- : DELETE /entity/:id
#### StateFactory(namespace, options)
Creates comprehensive initial state.
Parameters:
- namespace (string): Entity namespaceoptions
- (Object): Configuration options
Options:
- includeEntityState (boolean): Include entity-specific stateincludePagination
- (boolean): Include pagination stateincludeFiltering
- (boolean): Include filtering state includeSorting
- (boolean): Include sorting stateincludeSelection
- (boolean): Include selection stateincludeValidation
- (boolean): Include validation stateincludeMetadata
- (boolean): Include metadata statecustomFields
- (Object): Custom fields to add
#### Preset Methods
- StateFactory.minimal(namespace): Minimal state structureStateFactory.forList(namespace)
- : List/table optimized stateStateFactory.forForm(namespace)
- : Form optimized stateStateFactory.forRealTime(namespace)
- : Real-time data stateStateFactory.normalized(namespace)
- : Normalized entities stateStateFactory.withOperations(namespace, operations)
- : Custom operations tracking
#### CacheFactory(options)
Creates cache instance.
Options:
- type (string): 'local' or 'session'prefix
- (string): Key prefix for namespacingenableLogging
- (boolean): Enable debug loggingfallbackToMemory
- (boolean): Use memory storage fallbackmaxRetries
- (number): Maximum retry attempts
#### Methods
- set(key, value): Store valueget(key, defaultValue)
- : Retrieve valueremove(key)
- : Remove valueclear()
- : Clear all valueskeys()
- : Get all keyshas(key)
- : Check if key existsgetStats()
- : Get storage statisticsbulk.set(items)
- : Set multiple itemsbulk.get(keys)
- : Get multiple itemsbulk.remove(keys)
- : Remove multiple items
#### ApiFactory(sliceName, options)
Creates an RTK Query API factory instance.
Parameters:
- sliceName (string): Entity/slice nameoptions
- (Object): Configuration options
Options:
- tagTypes (string[]): Cache tag types for invalidationbaseQuery
- (Object): Custom base query configuration includeStandardEndpoints
- (boolean): Include CRUD endpoints
#### Static Methods
- createComplete(sliceName, options): Creates API with all standard endpointscreateCustom(sliceName, endpoints, options)
- : Creates API with only custom endpointsgetStandardEndpoints()
- : Returns standard endpoint configurationsgetHttpMethods()
- : Returns HTTP methods constants
#### Instance Methods
- inject(name, config): Inject single endpointinjectMany(endpoints)
- : Inject multiple endpoints create()
- : Create the final APIcreateStandard()
- : Add standard CRUD endpointscreateBulk()
- : Add bulk operation endpointscreateCustom(name, config)
- : Add custom endpointwithAuth(token)
- : Create API with authenticationwithBaseUrl(url)
- : Create API with custom base URL
#### Standard Endpoints Generated
- getAll: GET /entity/allgetById
- : GET /entity/:idcreate
- : POST /entityupdate
- : PUT /entity/:id patch
- : PATCH /entity/:iddelete
- : DELETE /entity/:idsearch
- : GET /entity/search
`javascript
// userFeature.js
import { FeatureFactory } from './FeatureFactory';
import StateFactory from './StateFactory';
import ThunkFactory from './ThunkFactory';
// Create state optimized for user lists
const initialState = StateFactory.forList('users');
// Create custom service actions
const userService = {
fetchProfile: ThunkFactory.custom('users', 'profile', {
method: 'GET',
customUrl: 'auth/profile'
}),
updateProfile: ThunkFactory.put('users', 'profile'),
changePassword: ThunkFactory.post('users', 'change-password')
};
// Create feature factory
const userFeature = FeatureFactory('users', {
setActiveUser: (state, action) => {
state.activeUser = action.payload;
},
clearUsers: (state) => {
state.data = [];
state.usersList = [];
}
});
// Generate the complete feature
const { slice, asyncActions } = userFeature.create({
service: userService,
initialState
});
export const userReducer = slice.reducer;
export const userActions = { ...slice.actions, ...asyncActions };
`
`javascript
// cacheService.js
import CacheFactory from './CacheFactory';
// Create application cache
const appCache = CacheFactory({
type: 'local',
prefix: 'myapp_',
enableLogging: process.env.NODE_ENV === 'development'
});
// Cache service
export const cacheService = {
async cacheUserData(userId, userData) {
await appCache.set(user_${userId}, userData);
},
async getUserData(userId) {
return await appCache.get(user_${userId});
},
async clearUserCache(userId) {
await appCache.remove(user_${userId});
},
async cacheUserPreferences(preferences) {
await appCache.set('user_preferences', preferences);
}
};
`
`javascript
// productsApi.js
import ApiFactory from './ApiFactory';
// Create complete API with all CRUD operations
export const productsApi = ApiFactory.createComplete('products', {
tagTypes: ['Product', 'Category', 'Review'],
baseQuery: {
baseUrl: process.env.REACT_APP_API_URL,
prepareHeaders: (headers, { getState }) => {
const token = getState().auth.token;
if (token) {
headers.set('authorization', Bearer ${token});
}
return headers;
}
}
});
// Add custom endpoints
const enhancedApi = productsApi.injectEndpoints({
endpoints: (builder) => ({
getFeaturedProducts: builder.query({
query: () => 'products/featured',
providesTags: ['Product'],
}),
getProductsByCategory: builder.query({
query: (categoryId) => products/category/${categoryId},CATEGORY_${categoryId}
providesTags: (result, error, categoryId) => [
{ type: 'Product', id: },products/${id}/favorite
],
}),
toggleProductFavorite: builder.mutation({
query: ({ id, isFavorite }) => ({
url: ,
method: 'PATCH',
body: { isFavorite },
}),
invalidatesTags: (result, error, { id }) => [
{ type: 'Product', id },
],
}),
}),
});
// Export hooks for use in components
export const {
useGetAllQuery,
useGetByIdQuery,
useCreateMutation,
useUpdateMutation,
useDeleteMutation,
useGetFeaturedProductsQuery,
useGetProductsByCategoryQuery,
useToggleProductFavoriteMutation,
} = enhancedApi;
`
`javascript
// ProductList.jsx
import React from 'react';
import {
useGetAllQuery,
useDeleteMutation,
useGetFeaturedProductsQuery
} from '../api/productsApi';
const ProductList = () => {
const {
data: products = [],
error,
isLoading
} = useGetAllQuery();
const {
data: featuredProducts = []
} = useGetFeaturedProductsQuery();
const [deleteProduct, { isLoading: isDeleting }] = useDeleteMutation();
const handleDelete = async (productId) => {
try {
await deleteProduct({ id: productId }).unwrap();
// Success - cache automatically invalidated
} catch (error) {
console.error('Failed to delete product:', error);
}
};
if (isLoading) return
return (
$3
`javascript
// advancedState.js
import StateFactory from './StateFactory';// Create state for a complex dashboard
const dashboardState = StateFactory('dashboard', {
includeMetadata: true,
includePagination: true,
customFields: {
widgets: [],
layout: 'grid',
theme: 'light',
notifications: [],
isFullscreen: false,
lastRefresh: null
}
});
// Create form state with validation
const userFormState = StateFactory.forForm('userForm');
// Create real-time chat state
const chatState = StateFactory.forRealTime('chat');
`Best Practices
$3
Use consistent, descriptive entity names:
`javascript
// Good
const userFeature = FeatureFactory('users');
const productFeature = FeatureFactory('products');// Avoid
const feature1 = FeatureFactory('u');
const myFeature = FeatureFactory('thing');
`$3
Choose appropriate state factories for your use case:
`javascript
// For data tables/lists
const listState = StateFactory.forList('products');// For forms
const formState = StateFactory.forForm('userForm');
// For simple data
const minimalState = StateFactory.minimal('settings');
`$3
Use descriptive prefixes for custom actions:
`javascript
const customActions = {
fetchUserProfile: ThunkFactory.create('users', 'profile'),
searchActiveUsers: ThunkFactory.create('users', 'active-search'),
bulkUpdateUsers: ThunkFactory.post('users', 'bulk-update')
};
`$3
Always handle async action states in components:
`javascript
const { data, loading, error } = useSelector(state => state.users);if (loading) return ;
if (error) return ;
`$3
Use appropriate cache types and prefixes:
`javascript
// Session data (cleared on browser close)
const sessionCache = CacheFactory({ type: 'session' });// Persistent data
const persistentCache = CacheFactory({
type: 'local',
prefix: 'app_v1_'
});
`$3
Choose the appropriate data fetching approach:`javascript
// RTK Query - Recommended for new projects (automatic caching, background refetching)
const usersApi = ApiFactory.createComplete('users');// Async Thunks - Better for complex business logic or existing codebases
const userFeature = FeatureFactory('users').create({
service: ThunkFactory.createCrud('users')
});
`$3
Configure APIs with proper error handling and authentication:
`javascript
const api = ApiFactory('products', {
baseQuery: {
baseUrl: process.env.REACT_APP_API_URL,
prepareHeaders: (headers, { getState }) => {
const token = getState().auth.token;
if (token) {
headers.set('authorization', Bearer ${token});
}
return headers;
},
},
tagTypes: ['Product', 'Category']
});
`TypeScript Support
The factories can be used with TypeScript by defining interfaces:
`typescript
interface User {
id: number;
name: string;
email: string;
}interface UserState {
users: User[];
currentUser: User | null;
loading: boolean;
error: string | null;
}
// Use with proper typing
const userFeature = FeatureFactory('users', reducers);
`Contributing
1. Fork the repository
2. Create your feature branch (
git checkout -b feature/amazing-feature)
3. Commit your changes (git commit -m 'Add amazing feature')
4. Push to the branch (git push origin feature/amazing-feature)
5. Open a Pull RequestLicense
This project is licensed under the MIT License - see the LICENSE file for details.
Dependencies
-
@reduxjs/toolkit` - Required for Redux functionality- Chrome 60+
- Firefox 55+
- Safari 12+
- Edge 79+
For older browsers, the CacheFactory will automatically fallback to memory storage if localStorage/sessionStorage is unavailable.