Skip to content

Commit 9db358a

Browse files
committed
docs: add api helper guide
1 parent e867876 commit 9db358a

File tree

1 file changed

+154
-4
lines changed

1 file changed

+154
-4
lines changed

README.md

Lines changed: 154 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Inspiration: [Ducks: Redux Reducer Bundles](https://github.com/erikras/ducks-mod
2828
- [Usage with redux-thunk](#usage-with-redux-thunk)
2929
- [Usage with redux-saga](#usage-with-redux-saga)
3030
- [Example with everything](#example-with-everything)
31+
- [Advanced](#advanced)
3132
- [API](#api)
3233
- [Other similar libraries](#other-similar-libraries)
3334
- [Caveats](#caveats)
@@ -63,6 +64,8 @@ in a more modular way while providing some additional utilities such as [depende
6364

6465
## Usage
6566

67+
Reducktion minimizes the amount of boilerplate by firstly skipping manually defining action types entirely, and secondly merging actions and reducers together.
68+
6669
```javascript
6770
// order.duck.js
6871

@@ -270,11 +273,12 @@ const duck = createDuck({
270273
...state,
271274
darkModeEnabled: !state.darkModeEnabled,
272275
}),
273-
274-
// Thunks here
276+
}),
277+
thunks: {
278+
// Thunks here
275279
someThunk,
276280
otherThunkWithInjects,
277-
}),
281+
},
278282
});
279283

280284
// Thunks
@@ -436,8 +440,8 @@ const duck = createDuck({
436440
hasError: false,
437441
orders: action.payload,
438442
}),
439-
archiveOrders, // Some random thunk
440443
}),
444+
thunks: { archiveOrders }, // Some random thunk
441445
reactions: ({ deps }) => ({
442446
[deps.user.types.logout]: state => ({ ...state, orders: [] }),
443447
}),
@@ -476,8 +480,154 @@ function* fetchOrdersSaga() {
476480
export default duck;
477481
```
478482

483+
## Advanced
484+
485+
### API actions helper
486+
487+
Nowadays, many websites are SPAs (Single Page Applications) and have to get some data from an API to show to the users.
488+
This data fetching process usually consists of three stages: `loading the data`, `receiving the data`, and `handling errors`.
489+
490+
Reducktion provides some higher level helpers to make handling any API related actions
491+
less laborious.
492+
493+
Let's first see what we would normally need to create the necessary things for fetching some imaginary **orders** and taking into account the three stages mentioned earlier.
494+
495+
```js
496+
import { createDuck } from 'reducktion';
497+
498+
const duck = createDuck({
499+
name: 'order',
500+
state: {
501+
orders: [],
502+
isLoading: false,
503+
hasError: false,
504+
error: null,
505+
},
506+
actions: () => ({
507+
fetchOrders: state => ({
508+
...state,
509+
isLoading: true,
510+
}),
511+
failFetchOrders: (state, action) => ({
512+
...state,
513+
isLoading: false,
514+
hasError: true,
515+
error: action.payload,
516+
}),
517+
receiveOrders: (state, action) => ({
518+
...state,
519+
isLoading: false,
520+
hasError: false,
521+
error: null,
522+
orders: action.payload,
523+
}),
524+
}),
525+
sagas: ({ types }) => [takeEvery([types.fetchOrders], fetchOrdersSaga)],
526+
});
527+
528+
// Sagas
529+
530+
function* fetchOrdersSaga() {
531+
try {
532+
const orders = yield call(api.fetchOrders);
533+
yield put(duck.actions.receiveOrders(orders));
534+
} catch (error) {
535+
yield put(duck.actions.failFetchOrders(error.message));
536+
}
537+
}
538+
```
539+
540+
That's quite a lot of setup / boilerplate for handling three stages of our API call.
541+
542+
Reducktion provides a helper action creator called `createApiAction` that can be used to create the same three actions as above baked into one enhanced action that behind the scenes updates the necessary state fields automatically by creating the individual reducers for you.
543+
544+
The simplest way to use `createApiAction` is to just give it the name of state field (eg. **orders**) where you want to save the data.
545+
By default the auto-created reducers will also update three fields during the different stages of the API call.
546+
547+
| Action | Description |
548+
| ---------------------- | -------------------------------------------------------------- |
549+
| `action()` | Starts the API request. |
550+
| `action.success(data)` | Finishes the API flow successfully and saves the data to state |
551+
| `action.fail(error?)` | Fails the API flow and saves the optional error data to state |
552+
553+
| State field | Description |
554+
| ---------------------------- | -------------------------------------------------------------------------------------------------------------- |
555+
| `isLoading` | Set to `true` when the API call is initiated, and set to `false` after success / failure. |
556+
| `hasError` | Set to `false` when when `action()` is called, and set to `true` when `action.fail()` is called. |
557+
| `error` | Set to `false` when when `action()` is called, and set to the param of `action.fail(error)` when it is called. |
558+
| `orders` (or something else) | Updated with the data `action.success(data)` is called. |
559+
560+
Example:
561+
562+
```js
563+
import { createDuck, createApiAction } from 'reducktion';
564+
565+
const duck = createDuck({
566+
name: 'order',
567+
state: {
568+
orders: [],
569+
isLoading: false,
570+
hasError: false,
571+
error: null,
572+
},
573+
actions: () => ({
574+
fetchOrders: createApiAction('orders'),
575+
}),
576+
sagas: ({ types }) => [takeEvery([types.fetchOrders], fetchOrdersSaga)],
577+
});
578+
579+
// Sagas
580+
581+
function* fetchOrdersSaga() {
582+
try {
583+
// In real world you would fetch orders from some API
584+
const orders = [];
585+
yield put(duck.actions.fetchOrders.success(orders));
586+
} catch (error) {
587+
yield put(duck.actions.fetchOrders.fail(error.message));
588+
}
589+
}
590+
```
591+
592+
However, in case you need more control over your state fields for the different stages or want to add your own you can achieve it by giving `createApiAction` a reducer definition object.
593+
594+
> NOTE: if you choose to use reducer definition object you always **have to** provide the `success` reducer! Other reducers are optional.
595+
596+
```js
597+
const duck = createDuck({
598+
// ...
599+
actions: () => ({
600+
fetchOrders: createApiAction({
601+
// REQUIRED!
602+
success: (state, action) => ({
603+
...state,
604+
orders: action.payload,
605+
isLoading: false,
606+
}),
607+
// optional
608+
loading: (state, action) => ({
609+
...state,
610+
isLoading: true,
611+
hasError: false,
612+
error: null,
613+
}),
614+
// optional
615+
failure: (state, action) => ({
616+
...state,
617+
isLoading: false,
618+
hasError: true,
619+
error: action.payload,
620+
}),
621+
}),
622+
}),
623+
// ...
624+
});
625+
```
626+
479627
## API
480628

629+
> TODO: UPDATE API DOCS!
630+
481631
Check out the more detailed [API documentation](API.md).
482632

483633
## Other similar libraries

0 commit comments

Comments
 (0)