with-http-reducer
v1.1.18
Published
Higher order reducer that adds standard async actions to a redux reducer
Downloads
7
Readme
With HTTP Reducer
Higher order reducer for generic FETCH_BEGIN
, FETCH_SUCCESS
, and FETCH_FAILURE
actions
$ npm install with-http-reducer
Todo
- fix module exporting so imports can be destructured
- organize
src
so helpers are more compact and easy to use - more tests
- update docs to describe functionality
- fix the issues with module exports
- add better typescript interfaces, enums, and types
Usage
Add helpers to a constants file
// users.constants.js import whr from 'with-http-reducer'; export const usersReducerName = 'users'; export const usersWithHttp = reducer => whr.withHttpReducer(reducer, usersReducerName); export const usersHttpBegin = payload => whr.httpBegin(usersReducerName, payload); export const usersHttpFailure = payload => whr.httpFailure(usersReducerName, payload); export const usersHttpSuccess = payload => whr.httpSuccess(usersReducerName, payload);
Add in higher order reducer:
import { usersWithHttp } from './users.constants'; const users = (state = { current: null }, { type, payload }) => { switch (type) { // any other case statements case 'SET_CURRENT_USER': return { ...state, current: payload }; default: return state; } }; export default usersWithHttp(users);
Use other helpers where you need them
// components import React, { useEffect } from 'react'; import { usersHttpBegin, selectUsersLoading } from './users.constants'; import { useSelector, useDispatch } from 'react-redux'; export default () => { const dispatch = useDispatch(); const loading = useSelector(selectUsersLoading); const httpError = useSelector(selectUsersHttpError); useEffect(() => { dispatch(usersHttpBegin()); }, [dispatch]); if (httpError) { // show error boundary throw new Error(httpError.message); } if (loading) { return <Spinner />; } return <div>content</div>; };
// thunks import { normalize } from 'normalizr'; import { usersHttpBegin, usersHttpSuccess, usersHttpFailure, usersReducerName } from './users.constants'; function fetchUsers() { return async dispatch => { dispatch(usersHttpBegin()); try { const res = await fetch(`someendpoint/users`); const users = await res.json(); const usersDictionary = normalize(users, usersSchema); dispatch( usersHttpSuccess({ byId: usersDictionary.entities[usersReducerName], allIds: usersDictionary.result }) ); } catch (err) { dispatch(usersHttpFailure({ err })); } }; }
// epics import { normalize } from 'normalizr'; import { switchMap, map, catchError } from 'rxjs/operators'; import { ajax } from 'rxjs/ajax'; import { ofType } from 'redux-observable'; import { usersHttpBegin, usersHttpSuccess, usersHttpFailure, usersReducerName } from './users.constants'; import { usersSchema } from './users.schema'; import { of } from 'rxjs'; export function fetchUsersEpic(action$) { return action$.pipe( ofType(usersHttpBegin().type), switchMap(() => { return ajax.getJSON(`someendpoint/users`).pipe( map(users => normalize(users, usersSchema)), map(({ entities, result }) => usersHttpSuccess({ byId: entities[usersReducerName], allIds: result }) ), catchError(err => of(usersHttpFailure({ err }))) ); }) ); }
// sagas import { normalize } from 'normalizr'; import { call, put, takeLatest } from 'redux-saga/effects'; import { usersHttpBegin, usersHttpSuccess, usersHttpFailure, usersReducerName } from './users.constants'; const api = url => fetch(url).then(res => res.json()); function* handleFetchUsersBeginAsync() { try { const users = yield call(api, `someendpoint/users`); const usersDictionary = yield normalize(users, usersSchema); yield put( usersHttpSuccess({ byId: usersDictionary.entities[usersReducerName], allIds: usersDictionary.result }) ); } catch (err) { put(usersHttpFailure({ err })); } } export function* watchFetchUsersBegin() { yield takeLatest(usersHttpBegin().type, handleFetchUsersBeginAsync); }