@feardread/feature-factory 4.0.2 → 4.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +677 -0
- package/dist/bundle.min.js +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/factory/api.js +3 -2
- package/src/factory/thunk.js +11 -2
package/README.md
ADDED
|
@@ -0,0 +1,677 @@
|
|
|
1
|
+
# Redux Factory Suite
|
|
2
|
+
|
|
3
|
+
A 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.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Overview](#overview)
|
|
8
|
+
- [Installation](#installation)
|
|
9
|
+
- [Core Components](#core-components)
|
|
10
|
+
- [Quick Start](#quick-start)
|
|
11
|
+
- [API Reference](#api-reference)
|
|
12
|
+
- [Examples](#examples)
|
|
13
|
+
- [Best Practices](#best-practices)
|
|
14
|
+
- [Contributing](#contributing)
|
|
15
|
+
|
|
16
|
+
## Overview
|
|
17
|
+
|
|
18
|
+
The Redux Factory Suite consists of five main components:
|
|
19
|
+
|
|
20
|
+
1. **FeatureFactory** - Creates complete Redux features with slices, async actions, and reducers
|
|
21
|
+
2. **ThunkFactory** - Generates standardized async thunks for API operations
|
|
22
|
+
3. **StateFactory** - Creates comprehensive initial state structures
|
|
23
|
+
4. **CacheFactory** - Provides browser storage utilities with fallback support
|
|
24
|
+
5. **ApiFactory** - Creates RTK Query APIs with standardized CRUD operations and caching
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install @feardread/feature-factory
|
|
30
|
+
# The factory utilities are included in your project source
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
dependencies:
|
|
34
|
+
```bash
|
|
35
|
+
npm install @reduxjs/toolkit react-redux
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Core Components
|
|
39
|
+
|
|
40
|
+
### FeatureFactory
|
|
41
|
+
|
|
42
|
+
Creates complete Redux features with standardized structure including slices, async actions, and reducers.
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
import { FeatureFactory } from './FeatureFactory';
|
|
46
|
+
|
|
47
|
+
const userFeature = FeatureFactory('users', {
|
|
48
|
+
setCurrentUser: (state, action) => {
|
|
49
|
+
state.currentUser = action.payload;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const { slice, asyncActions } = userFeature.create();
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### ThunkFactory
|
|
57
|
+
|
|
58
|
+
Factory for creating Redux async thunks with standardized patterns for HTTP operations.
|
|
59
|
+
|
|
60
|
+
```javascript
|
|
61
|
+
import ThunkFactory from './ThunkFactory';
|
|
62
|
+
|
|
63
|
+
// Create individual thunks
|
|
64
|
+
const fetchUsers = ThunkFactory.create('users', 'all');
|
|
65
|
+
const createUser = ThunkFactory.post('users', 'create');
|
|
66
|
+
|
|
67
|
+
// Create full CRUD suite
|
|
68
|
+
const userThunks = ThunkFactory.createCrud('users');
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### StateFactory
|
|
72
|
+
|
|
73
|
+
Creates comprehensive initial state structures with optional features like pagination, filtering, and validation.
|
|
74
|
+
|
|
75
|
+
```javascript
|
|
76
|
+
import StateFactory from './StateFactory';
|
|
77
|
+
|
|
78
|
+
// Full-featured state
|
|
79
|
+
const initialState = StateFactory('users');
|
|
80
|
+
|
|
81
|
+
// Minimal state
|
|
82
|
+
const minimalState = StateFactory.minimal('users');
|
|
83
|
+
|
|
84
|
+
// List-optimized state
|
|
85
|
+
const listState = StateFactory.forList('users');
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### CacheFactory
|
|
89
|
+
|
|
90
|
+
Browser storage utilities with automatic fallback to memory storage.
|
|
91
|
+
|
|
92
|
+
```javascript
|
|
93
|
+
import CacheFactory from './CacheFactory';
|
|
94
|
+
|
|
95
|
+
const cache = CacheFactory({ type: 'local', prefix: 'myapp_' });
|
|
96
|
+
|
|
97
|
+
await cache.set('user', userData);
|
|
98
|
+
const user = await cache.get('user');
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### ApiFactory
|
|
102
|
+
|
|
103
|
+
Creates RTK Query APIs with standardized CRUD operations, caching, and tag invalidation.
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
import ApiFactory from './ApiFactory';
|
|
107
|
+
|
|
108
|
+
// Create complete API with standard CRUD endpoints
|
|
109
|
+
const usersApi = ApiFactory.createComplete('users');
|
|
110
|
+
|
|
111
|
+
// Create custom API
|
|
112
|
+
const customApi = ApiFactory('products')
|
|
113
|
+
.inject('getFeatured', {
|
|
114
|
+
method: 'GET',
|
|
115
|
+
url: 'products/featured',
|
|
116
|
+
providesTags: ['Product']
|
|
117
|
+
})
|
|
118
|
+
.create();
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Quick Start
|
|
122
|
+
|
|
123
|
+
Here's how to create a complete feature using all components:
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
import { FeatureFactory } from './FeatureFactory';
|
|
127
|
+
import StateFactory from './StateFactory';
|
|
128
|
+
import ThunkFactory from './ThunkFactory';
|
|
129
|
+
import ApiFactory from './ApiFactory';
|
|
130
|
+
|
|
131
|
+
// 1. Create RTK Query API (recommended for new projects)
|
|
132
|
+
const productsApi = ApiFactory.createComplete('products', {
|
|
133
|
+
tagTypes: ['Product', 'Category']
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// OR use traditional async thunks
|
|
137
|
+
// 1. Create initial state
|
|
138
|
+
const initialState = StateFactory.forList('products');
|
|
139
|
+
|
|
140
|
+
// 2. Create custom async actions
|
|
141
|
+
const customActions = {
|
|
142
|
+
searchProducts: ThunkFactory.create('products', 'search'),
|
|
143
|
+
getPopularProducts: ThunkFactory.custom('products', 'popular', {
|
|
144
|
+
method: 'GET',
|
|
145
|
+
customUrl: 'products/popular'
|
|
146
|
+
})
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// 3. Create the feature
|
|
150
|
+
const productFeature = FeatureFactory('products', {
|
|
151
|
+
toggleFavorite: (state, action) => {
|
|
152
|
+
const product = state.products.find(p => p.id === action.payload);
|
|
153
|
+
if (product) {
|
|
154
|
+
product.isFavorite = !product.isFavorite;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// 4. Generate slice and actions
|
|
160
|
+
const { slice, asyncActions } = productFeature.create({
|
|
161
|
+
service: customActions,
|
|
162
|
+
initialState
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
export const productReducer = slice.reducer;
|
|
166
|
+
export const productActions = { ...slice.actions, ...asyncActions };
|
|
167
|
+
|
|
168
|
+
// For RTK Query approach, use generated hooks:
|
|
169
|
+
// const { data, error, isLoading } = productsApi.useGetAllQuery();
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## API Reference
|
|
173
|
+
|
|
174
|
+
### FeatureFactory API
|
|
175
|
+
|
|
176
|
+
#### `FeatureFactory(entity, reducers, endpoints)`
|
|
177
|
+
|
|
178
|
+
Creates a feature factory instance.
|
|
179
|
+
|
|
180
|
+
**Parameters:**
|
|
181
|
+
- `entity` (string): Entity name for the feature
|
|
182
|
+
- `reducers` (Object): Custom reducers to include in the slice
|
|
183
|
+
- `endpoints` (Object|null): API endpoints (currently unused)
|
|
184
|
+
|
|
185
|
+
**Returns:** Feature factory instance
|
|
186
|
+
|
|
187
|
+
#### Methods
|
|
188
|
+
|
|
189
|
+
- `create(options)`: Creates slice and async actions
|
|
190
|
+
- `manager(initialReducers)`: Creates dynamic reducer manager
|
|
191
|
+
- `inject(source, destination)`: Merges objects
|
|
192
|
+
- `getEntityName()`: Returns entity name
|
|
193
|
+
- `getAdapter()`: Returns RTK entity adapter
|
|
194
|
+
|
|
195
|
+
### ThunkFactory API
|
|
196
|
+
|
|
197
|
+
#### Static Methods
|
|
198
|
+
|
|
199
|
+
- `create(entity, prefix)`: Creates GET request thunk
|
|
200
|
+
- `post(entity, prefix)`: Creates POST request thunk
|
|
201
|
+
- `put(entity, prefix)`: Creates PUT request thunk
|
|
202
|
+
- `patch(entity, prefix)`: Creates PATCH request thunk
|
|
203
|
+
- `delete(entity, prefix)`: Creates DELETE request thunk
|
|
204
|
+
- `custom(entity, prefix, options)`: Creates custom configured thunk
|
|
205
|
+
- `createCrud(entity)`: Creates complete CRUD thunk suite
|
|
206
|
+
|
|
207
|
+
#### Standard Operations
|
|
208
|
+
|
|
209
|
+
- `all`: GET /entity/all
|
|
210
|
+
- `one`: GET /entity/:id
|
|
211
|
+
- `search`: GET /entity/search?params
|
|
212
|
+
- `create`: POST /entity/create
|
|
213
|
+
- `update`: PUT /entity/:id
|
|
214
|
+
- `patch`: PATCH /entity/:id
|
|
215
|
+
- `delete`: DELETE /entity/:id
|
|
216
|
+
|
|
217
|
+
### StateFactory API
|
|
218
|
+
|
|
219
|
+
#### `StateFactory(namespace, options)`
|
|
220
|
+
|
|
221
|
+
Creates comprehensive initial state.
|
|
222
|
+
|
|
223
|
+
**Parameters:**
|
|
224
|
+
- `namespace` (string): Entity namespace
|
|
225
|
+
- `options` (Object): Configuration options
|
|
226
|
+
|
|
227
|
+
**Options:**
|
|
228
|
+
- `includeEntityState` (boolean): Include entity-specific state
|
|
229
|
+
- `includePagination` (boolean): Include pagination state
|
|
230
|
+
- `includeFiltering` (boolean): Include filtering state
|
|
231
|
+
- `includeSorting` (boolean): Include sorting state
|
|
232
|
+
- `includeSelection` (boolean): Include selection state
|
|
233
|
+
- `includeValidation` (boolean): Include validation state
|
|
234
|
+
- `includeMetadata` (boolean): Include metadata state
|
|
235
|
+
- `customFields` (Object): Custom fields to add
|
|
236
|
+
|
|
237
|
+
#### Preset Methods
|
|
238
|
+
|
|
239
|
+
- `StateFactory.minimal(namespace)`: Minimal state structure
|
|
240
|
+
- `StateFactory.forList(namespace)`: List/table optimized state
|
|
241
|
+
- `StateFactory.forForm(namespace)`: Form optimized state
|
|
242
|
+
- `StateFactory.forRealTime(namespace)`: Real-time data state
|
|
243
|
+
- `StateFactory.normalized(namespace)`: Normalized entities state
|
|
244
|
+
- `StateFactory.withOperations(namespace, operations)`: Custom operations tracking
|
|
245
|
+
|
|
246
|
+
### CacheFactory API
|
|
247
|
+
|
|
248
|
+
#### `CacheFactory(options)`
|
|
249
|
+
|
|
250
|
+
Creates cache instance.
|
|
251
|
+
|
|
252
|
+
**Options:**
|
|
253
|
+
- `type` (string): 'local' or 'session'
|
|
254
|
+
- `prefix` (string): Key prefix for namespacing
|
|
255
|
+
- `enableLogging` (boolean): Enable debug logging
|
|
256
|
+
- `fallbackToMemory` (boolean): Use memory storage fallback
|
|
257
|
+
- `maxRetries` (number): Maximum retry attempts
|
|
258
|
+
|
|
259
|
+
#### Methods
|
|
260
|
+
|
|
261
|
+
- `set(key, value)`: Store value
|
|
262
|
+
- `get(key, defaultValue)`: Retrieve value
|
|
263
|
+
- `remove(key)`: Remove value
|
|
264
|
+
- `clear()`: Clear all values
|
|
265
|
+
- `keys()`: Get all keys
|
|
266
|
+
- `has(key)`: Check if key exists
|
|
267
|
+
- `getStats()`: Get storage statistics
|
|
268
|
+
- `bulk.set(items)`: Set multiple items
|
|
269
|
+
- `bulk.get(keys)`: Get multiple items
|
|
270
|
+
- `bulk.remove(keys)`: Remove multiple items
|
|
271
|
+
|
|
272
|
+
### ApiFactory API
|
|
273
|
+
|
|
274
|
+
#### `ApiFactory(sliceName, options)`
|
|
275
|
+
|
|
276
|
+
Creates an RTK Query API factory instance.
|
|
277
|
+
|
|
278
|
+
**Parameters:**
|
|
279
|
+
- `sliceName` (string): Entity/slice name
|
|
280
|
+
- `options` (Object): Configuration options
|
|
281
|
+
|
|
282
|
+
**Options:**
|
|
283
|
+
- `tagTypes` (string[]): Cache tag types for invalidation
|
|
284
|
+
- `baseQuery` (Object): Custom base query configuration
|
|
285
|
+
- `includeStandardEndpoints` (boolean): Include CRUD endpoints
|
|
286
|
+
|
|
287
|
+
#### Static Methods
|
|
288
|
+
|
|
289
|
+
- `createComplete(sliceName, options)`: Creates API with all standard endpoints
|
|
290
|
+
- `createCustom(sliceName, endpoints, options)`: Creates API with only custom endpoints
|
|
291
|
+
- `getStandardEndpoints()`: Returns standard endpoint configurations
|
|
292
|
+
- `getHttpMethods()`: Returns HTTP methods constants
|
|
293
|
+
|
|
294
|
+
#### Instance Methods
|
|
295
|
+
|
|
296
|
+
- `inject(name, config)`: Inject single endpoint
|
|
297
|
+
- `injectMany(endpoints)`: Inject multiple endpoints
|
|
298
|
+
- `create()`: Create the final API
|
|
299
|
+
- `createStandard()`: Add standard CRUD endpoints
|
|
300
|
+
- `createBulk()`: Add bulk operation endpoints
|
|
301
|
+
- `createCustom(name, config)`: Add custom endpoint
|
|
302
|
+
- `withAuth(token)`: Create API with authentication
|
|
303
|
+
- `withBaseUrl(url)`: Create API with custom base URL
|
|
304
|
+
|
|
305
|
+
#### Standard Endpoints Generated
|
|
306
|
+
|
|
307
|
+
- `getAll`: GET /entity/all
|
|
308
|
+
- `getById`: GET /entity/:id
|
|
309
|
+
- `create`: POST /entity
|
|
310
|
+
- `update`: PUT /entity/:id
|
|
311
|
+
- `patch`: PATCH /entity/:id
|
|
312
|
+
- `delete`: DELETE /entity/:id
|
|
313
|
+
- `search`: GET /entity/search
|
|
314
|
+
|
|
315
|
+
## Examples
|
|
316
|
+
|
|
317
|
+
### Complete User Management Feature
|
|
318
|
+
|
|
319
|
+
```javascript
|
|
320
|
+
// userFeature.js
|
|
321
|
+
import { FeatureFactory } from './FeatureFactory';
|
|
322
|
+
import StateFactory from './StateFactory';
|
|
323
|
+
import ThunkFactory from './ThunkFactory';
|
|
324
|
+
|
|
325
|
+
// Create state optimized for user lists
|
|
326
|
+
const initialState = StateFactory.forList('users');
|
|
327
|
+
|
|
328
|
+
// Create custom service actions
|
|
329
|
+
const userService = {
|
|
330
|
+
fetchProfile: ThunkFactory.custom('users', 'profile', {
|
|
331
|
+
method: 'GET',
|
|
332
|
+
customUrl: 'auth/profile'
|
|
333
|
+
}),
|
|
334
|
+
updateProfile: ThunkFactory.put('users', 'profile'),
|
|
335
|
+
changePassword: ThunkFactory.post('users', 'change-password')
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
// Create feature factory
|
|
339
|
+
const userFeature = FeatureFactory('users', {
|
|
340
|
+
setActiveUser: (state, action) => {
|
|
341
|
+
state.activeUser = action.payload;
|
|
342
|
+
},
|
|
343
|
+
clearUsers: (state) => {
|
|
344
|
+
state.data = [];
|
|
345
|
+
state.usersList = [];
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// Generate the complete feature
|
|
350
|
+
const { slice, asyncActions } = userFeature.create({
|
|
351
|
+
service: userService,
|
|
352
|
+
initialState
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
export const userReducer = slice.reducer;
|
|
356
|
+
export const userActions = { ...slice.actions, ...asyncActions };
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Using the Cache System
|
|
360
|
+
|
|
361
|
+
```javascript
|
|
362
|
+
// cacheService.js
|
|
363
|
+
import CacheFactory from './CacheFactory';
|
|
364
|
+
|
|
365
|
+
// Create application cache
|
|
366
|
+
const appCache = CacheFactory({
|
|
367
|
+
type: 'local',
|
|
368
|
+
prefix: 'myapp_',
|
|
369
|
+
enableLogging: process.env.NODE_ENV === 'development'
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// Cache service
|
|
373
|
+
export const cacheService = {
|
|
374
|
+
async cacheUserData(userId, userData) {
|
|
375
|
+
await appCache.set(`user_${userId}`, userData);
|
|
376
|
+
},
|
|
377
|
+
|
|
378
|
+
async getUserData(userId) {
|
|
379
|
+
return await appCache.get(`user_${userId}`);
|
|
380
|
+
},
|
|
381
|
+
|
|
382
|
+
async clearUserCache(userId) {
|
|
383
|
+
await appCache.remove(`user_${userId}`);
|
|
384
|
+
},
|
|
385
|
+
|
|
386
|
+
async cacheUserPreferences(preferences) {
|
|
387
|
+
await appCache.set('user_preferences', preferences);
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### RTK Query API with ApiFactory
|
|
393
|
+
|
|
394
|
+
```javascript
|
|
395
|
+
// productsApi.js
|
|
396
|
+
import ApiFactory from './ApiFactory';
|
|
397
|
+
|
|
398
|
+
// Create complete API with all CRUD operations
|
|
399
|
+
export const productsApi = ApiFactory.createComplete('products', {
|
|
400
|
+
tagTypes: ['Product', 'Category', 'Review'],
|
|
401
|
+
baseQuery: {
|
|
402
|
+
baseUrl: process.env.REACT_APP_API_URL,
|
|
403
|
+
prepareHeaders: (headers, { getState }) => {
|
|
404
|
+
const token = getState().auth.token;
|
|
405
|
+
if (token) {
|
|
406
|
+
headers.set('authorization', `Bearer ${token}`);
|
|
407
|
+
}
|
|
408
|
+
return headers;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// Add custom endpoints
|
|
414
|
+
const enhancedApi = productsApi.injectEndpoints({
|
|
415
|
+
endpoints: (builder) => ({
|
|
416
|
+
getFeaturedProducts: builder.query({
|
|
417
|
+
query: () => 'products/featured',
|
|
418
|
+
providesTags: ['Product'],
|
|
419
|
+
}),
|
|
420
|
+
getProductsByCategory: builder.query({
|
|
421
|
+
query: (categoryId) => `products/category/${categoryId}`,
|
|
422
|
+
providesTags: (result, error, categoryId) => [
|
|
423
|
+
{ type: 'Product', id: `CATEGORY_${categoryId}` },
|
|
424
|
+
],
|
|
425
|
+
}),
|
|
426
|
+
toggleProductFavorite: builder.mutation({
|
|
427
|
+
query: ({ id, isFavorite }) => ({
|
|
428
|
+
url: `products/${id}/favorite`,
|
|
429
|
+
method: 'PATCH',
|
|
430
|
+
body: { isFavorite },
|
|
431
|
+
}),
|
|
432
|
+
invalidatesTags: (result, error, { id }) => [
|
|
433
|
+
{ type: 'Product', id },
|
|
434
|
+
],
|
|
435
|
+
}),
|
|
436
|
+
}),
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
// Export hooks for use in components
|
|
440
|
+
export const {
|
|
441
|
+
useGetAllQuery,
|
|
442
|
+
useGetByIdQuery,
|
|
443
|
+
useCreateMutation,
|
|
444
|
+
useUpdateMutation,
|
|
445
|
+
useDeleteMutation,
|
|
446
|
+
useGetFeaturedProductsQuery,
|
|
447
|
+
useGetProductsByCategoryQuery,
|
|
448
|
+
useToggleProductFavoriteMutation,
|
|
449
|
+
} = enhancedApi;
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
### Using ApiFactory in Components
|
|
453
|
+
|
|
454
|
+
```javascript
|
|
455
|
+
// ProductList.jsx
|
|
456
|
+
import React from 'react';
|
|
457
|
+
import {
|
|
458
|
+
useGetAllQuery,
|
|
459
|
+
useDeleteMutation,
|
|
460
|
+
useGetFeaturedProductsQuery
|
|
461
|
+
} from '../api/productsApi';
|
|
462
|
+
|
|
463
|
+
const ProductList = () => {
|
|
464
|
+
const {
|
|
465
|
+
data: products = [],
|
|
466
|
+
error,
|
|
467
|
+
isLoading
|
|
468
|
+
} = useGetAllQuery();
|
|
469
|
+
|
|
470
|
+
const {
|
|
471
|
+
data: featuredProducts = []
|
|
472
|
+
} = useGetFeaturedProductsQuery();
|
|
473
|
+
|
|
474
|
+
const [deleteProduct, { isLoading: isDeleting }] = useDeleteMutation();
|
|
475
|
+
|
|
476
|
+
const handleDelete = async (productId) => {
|
|
477
|
+
try {
|
|
478
|
+
await deleteProduct({ id: productId }).unwrap();
|
|
479
|
+
// Success - cache automatically invalidated
|
|
480
|
+
} catch (error) {
|
|
481
|
+
console.error('Failed to delete product:', error);
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
if (isLoading) return <div>Loading products...</div>;
|
|
486
|
+
if (error) return <div>Error: {error.message}</div>;
|
|
487
|
+
|
|
488
|
+
return (
|
|
489
|
+
<div>
|
|
490
|
+
<section>
|
|
491
|
+
<h2>Featured Products</h2>
|
|
492
|
+
{featuredProducts.map(product => (
|
|
493
|
+
<ProductCard key={product.id} product={product} />
|
|
494
|
+
))}
|
|
495
|
+
</section>
|
|
496
|
+
|
|
497
|
+
<section>
|
|
498
|
+
<h2>All Products</h2>
|
|
499
|
+
{products.map(product => (
|
|
500
|
+
<ProductCard
|
|
501
|
+
key={product.id}
|
|
502
|
+
product={product}
|
|
503
|
+
onDelete={() => handleDelete(product.id)}
|
|
504
|
+
isDeleting={isDeleting}
|
|
505
|
+
/>
|
|
506
|
+
))}
|
|
507
|
+
</section>
|
|
508
|
+
</div>
|
|
509
|
+
);
|
|
510
|
+
};
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
### Advanced State Management
|
|
514
|
+
|
|
515
|
+
```javascript
|
|
516
|
+
// advancedState.js
|
|
517
|
+
import StateFactory from './StateFactory';
|
|
518
|
+
|
|
519
|
+
// Create state for a complex dashboard
|
|
520
|
+
const dashboardState = StateFactory('dashboard', {
|
|
521
|
+
includeMetadata: true,
|
|
522
|
+
includePagination: true,
|
|
523
|
+
customFields: {
|
|
524
|
+
widgets: [],
|
|
525
|
+
layout: 'grid',
|
|
526
|
+
theme: 'light',
|
|
527
|
+
notifications: [],
|
|
528
|
+
isFullscreen: false,
|
|
529
|
+
lastRefresh: null
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
// Create form state with validation
|
|
534
|
+
const userFormState = StateFactory.forForm('userForm');
|
|
535
|
+
|
|
536
|
+
// Create real-time chat state
|
|
537
|
+
const chatState = StateFactory.forRealTime('chat');
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
## Best Practices
|
|
541
|
+
|
|
542
|
+
### 1. Entity Naming
|
|
543
|
+
Use consistent, descriptive entity names:
|
|
544
|
+
```javascript
|
|
545
|
+
// Good
|
|
546
|
+
const userFeature = FeatureFactory('users');
|
|
547
|
+
const productFeature = FeatureFactory('products');
|
|
548
|
+
|
|
549
|
+
// Avoid
|
|
550
|
+
const feature1 = FeatureFactory('u');
|
|
551
|
+
const myFeature = FeatureFactory('thing');
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
### 2. State Organization
|
|
555
|
+
Choose appropriate state factories for your use case:
|
|
556
|
+
```javascript
|
|
557
|
+
// For data tables/lists
|
|
558
|
+
const listState = StateFactory.forList('products');
|
|
559
|
+
|
|
560
|
+
// For forms
|
|
561
|
+
const formState = StateFactory.forForm('userForm');
|
|
562
|
+
|
|
563
|
+
// For simple data
|
|
564
|
+
const minimalState = StateFactory.minimal('settings');
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
### 3. Async Actions
|
|
568
|
+
Use descriptive prefixes for custom actions:
|
|
569
|
+
```javascript
|
|
570
|
+
const customActions = {
|
|
571
|
+
fetchUserProfile: ThunkFactory.create('users', 'profile'),
|
|
572
|
+
searchActiveUsers: ThunkFactory.create('users', 'active-search'),
|
|
573
|
+
bulkUpdateUsers: ThunkFactory.post('users', 'bulk-update')
|
|
574
|
+
};
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
### 4. Error Handling
|
|
578
|
+
Always handle async action states in components:
|
|
579
|
+
```javascript
|
|
580
|
+
const { data, loading, error } = useSelector(state => state.users);
|
|
581
|
+
|
|
582
|
+
if (loading) return <LoadingSpinner />;
|
|
583
|
+
if (error) return <ErrorMessage error={error} />;
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
### 5. Caching Strategy
|
|
587
|
+
Use appropriate cache types and prefixes:
|
|
588
|
+
```javascript
|
|
589
|
+
// Session data (cleared on browser close)
|
|
590
|
+
const sessionCache = CacheFactory({ type: 'session' });
|
|
591
|
+
|
|
592
|
+
// Persistent data
|
|
593
|
+
const persistentCache = CacheFactory({
|
|
594
|
+
type: 'local',
|
|
595
|
+
prefix: 'app_v1_'
|
|
596
|
+
});
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
### 6. RTK Query vs Async Thunks
|
|
600
|
+
Choose the appropriate data fetching approach:
|
|
601
|
+
|
|
602
|
+
```javascript
|
|
603
|
+
// RTK Query - Recommended for new projects (automatic caching, background refetching)
|
|
604
|
+
const usersApi = ApiFactory.createComplete('users');
|
|
605
|
+
|
|
606
|
+
// Async Thunks - Better for complex business logic or existing codebases
|
|
607
|
+
const userFeature = FeatureFactory('users').create({
|
|
608
|
+
service: ThunkFactory.createCrud('users')
|
|
609
|
+
});
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
### 7. API Configuration
|
|
613
|
+
Configure APIs with proper error handling and authentication:
|
|
614
|
+
```javascript
|
|
615
|
+
const api = ApiFactory('products', {
|
|
616
|
+
baseQuery: {
|
|
617
|
+
baseUrl: process.env.REACT_APP_API_URL,
|
|
618
|
+
prepareHeaders: (headers, { getState }) => {
|
|
619
|
+
const token = getState().auth.token;
|
|
620
|
+
if (token) {
|
|
621
|
+
headers.set('authorization', `Bearer ${token}`);
|
|
622
|
+
}
|
|
623
|
+
return headers;
|
|
624
|
+
},
|
|
625
|
+
},
|
|
626
|
+
tagTypes: ['Product', 'Category']
|
|
627
|
+
});
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
## TypeScript Support
|
|
631
|
+
|
|
632
|
+
The factories can be used with TypeScript by defining interfaces:
|
|
633
|
+
|
|
634
|
+
```typescript
|
|
635
|
+
interface User {
|
|
636
|
+
id: number;
|
|
637
|
+
name: string;
|
|
638
|
+
email: string;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
interface UserState {
|
|
642
|
+
users: User[];
|
|
643
|
+
currentUser: User | null;
|
|
644
|
+
loading: boolean;
|
|
645
|
+
error: string | null;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// Use with proper typing
|
|
649
|
+
const userFeature = FeatureFactory<UserState>('users', reducers);
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
## Contributing
|
|
653
|
+
|
|
654
|
+
1. Fork the repository
|
|
655
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
656
|
+
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
657
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
658
|
+
5. Open a Pull Request
|
|
659
|
+
|
|
660
|
+
## License
|
|
661
|
+
|
|
662
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
663
|
+
|
|
664
|
+
## Dependencies
|
|
665
|
+
|
|
666
|
+
- `@reduxjs/toolkit` - Required for Redux functionality
|
|
667
|
+
- Modern browser with ES6+ support
|
|
668
|
+
- Node.js 14+ for development
|
|
669
|
+
|
|
670
|
+
## Browser Support
|
|
671
|
+
|
|
672
|
+
- Chrome 60+
|
|
673
|
+
- Firefox 55+
|
|
674
|
+
- Safari 12+
|
|
675
|
+
- Edge 79+
|
|
676
|
+
|
|
677
|
+
For older browsers, the CacheFactory will automatically fallback to memory storage if localStorage/sessionStorage is unavailable.
|