@codejamboree/action-builder
v1.1.0
Published
Create redux actions
Downloads
5
Maintainers
Readme
action-builder
This is inspired by https://www.npmjs.com/package/redux-saga-routines. The stages for fetch operations are used in the same way.
When working with a specific slice in a Redux Store, you can create and enforce a standard set of actions with a common prefix.
import actionBuilder from '@codejamboree/action-builder';
const build = actionBuilder('settings');
export const hide = build('HIDE');
console.log(changeName());
// { type: 'settings HIDE', payload: undefined }
Actions payloads can be typed.
export const rename = build<{name: string}>('RENAME');
// @ts-expect-error
const action = rename({name: 3});
// Valid
const action = rename({name: 'Lewis Moten'});
console.log(action.payload.name);
// Lewis Moten
Fetch Actions
Common stages for a fetch can be created as a group for consistency. The stages are trigger, request, success, error, and fulfill. Each stage may have its payload typed.
// default payload types
export const addItem = build.fetch('ADD_ITEM');
// Typed payloads
export const addItem = build.fetch<
{ name: string }, // trigger payload
void, // request payload
{ id: number, name: string}, // success payload
{ error: string }, // error payload
void, // fulfill payload
>('ADD_ITEM');
Dispatching fetch actions
You can dispatch the stages of a fetch request throughout your application.
const dispatch = useDipatch();
dispatch(addItem.trigger({name: 'Lewis Moten'}));
// same as addItem.trigger
dispatch(addItem({name: 'Lewis Moten'}));
dispatch(addItem.request());
dispatch(addItem.success({id: 42, name: 'Lewis Moten'}));
dispatch(addItem.failure({ error: 'Duplicate name' }));
dispatch(addItem.fulfill());
Lifecycle of Fetch actions in Saga
The various stages of fetch actions are most useful to the common lifecycles of redux saga fetch operations.
Of special note is that the upper-case key of each stage (TRIGGER, REQUEST, SUCCESS, FAILURE, FULFILL) provides a short-cut to the type.
export default takeEvery(
addItem.TRIGGER, // or addItem.trigger.type
function* worker(action) {
yield put(addItem.request());
try {
const results = yield call(
apiPost, 'addItem', action.payload
);
yield put(addItem.success(results))
} catch (error) {
yield put(addItem.failure({ error }));
} finally {
yield put(addItem.fulfill());
}
}
);
Handle Stages With Reducers
Reducers may use the upper-case keys as well to reduce the action into the current state.
const addItemRequest = produce((
draft: ItemState,
action: ReturnType<typeof addItem.request>
) => {
draft.isLoading = true;
});
const addItemSuccess = produce((
draft: ItemState,
action: ReturnType<typeof addItem.success>
) => {
const item = action.payload;
draft.allIds.push(item.id);
draft.byId[item.id] = item;
});
const addItemFulfill = produce((
draft: ItemState,
action: ReturnType<typeof addItem.fulfill>
) => {
draft.isLoading = false;
});
handleActions({
[addItem.REQUEST]: addItemRequest,
[addItem.SUCCESS]: addItemSuccess,
[addItem.FULFILL]: addItemFulfill
},
initialState
);
Progress
In addition to the stages for fetch operations, the action builder can also build progress/abort actions. Here is how to create progress actions, and combine them with fetch actions.
const readProgress = build.progress<
{ count: number, total: number }, // progress
void, // abort
>('READ');
const readFetch = build.fetch<
void, // trigger
void, // request
{id: number, name: string}[], // success
{error: string}, // failure
void // fulfill
>('READ');
export const read = Object.assign(readFetch, readProgress);
Drag & Drop
Basic Drag & Drop operations. Similar to the stages for fetch, the names of each operation have been kept to the same length, primarily for estetics & consistency.
const itemDnd = build.dnd<
{ dragId: number }, // Drag
{ dragId: number, dropId: number} // Drop
{ dragId: number, dropId: number} // Over
{ dragId: number } // Quit
{ dragId: number, dropId? number } // Pass
>('ITEM', 'DND');
// Drag Start
itemDnd.drag({ dragId: 1 });
// Dragging Over
itemDnd.over({ dragId: 1, dropId: 2 });
// Dragging Over invalid drop target
itemDnd.pass({ dragId: 1 });
// Cancel Drag and revert its effects
itemDnd.quit({ dragId: 1 });
// Drop
itemDnd.drop({ dragId: 1, dropId: 2});