Network history proposal
See original GitHub issueHello !
First of all thank you for this package. Our application has many api requests calls and this is going to make our life much simpler.
After playing with your library we realized we could make something we called “network history”. It is basically a root reducer which takes all the actions (success, error, request) and with that it fills a network tree in the state that later we use to populate our app with selectors.
Here is the code I have so far:
import {createRequestInstance, watchRequests} from "redux-saga-requests/src";
import {isRequestAction} from "redux-saga-requests/src/sagas";
import axiosConfig from './axiosConfig'
const initialState = {};
/**
* All network response actions has meta.request object in the payload
*/
const isNetworkResponseAction = action =>
action.payload
&& action.payload.meta
&& action.payload.meta.request
/**
* A success network action always has data in the meta
*/
const isSuccessAction = action => isNetworkResponseAction(action) && action.payload.data;
/**
* An error network action always has error in the meta
*/
const isErrorAction = action => isNetworkResponseAction(action) && action.payload.error;
/**
* REDUCER
*/
export default function network(state = initialState, action) {
if (isRequestAction(action)) {
const {type} = action;
return {
...state,
[type]: {
...state[type],
fetching: true,
},
}
}
if (isSuccessAction(action)) {
return {
...state,
[action.payload.meta.type]: {
fetching: false,
data: action.payload.data,
error: '',
},
}
}
if (isErrorAction(action)) {
return {
...state,
[action.payload.meta.type]: {
...state[action.payload.meta.type],
fetching: false,
error: 'Error',
},
}
}
//We can also have _ABORT action but we still not need it
return state;
}
/**
* SELECTORS
*/
export const selectNetworkResource = (state, resource: string) => state.network[resource] && state.network[resource].data
/**
* LISTENERS
*/
export function* networkListeners() {
yield createRequestInstance(axiosConfig)
yield watchRequests();
}
After adding this network
reducer in the root reducer with combineReducers
we have a tree like this when we have one success call:
So now we can use a selector in mapStateToProps
to access the data:
const mapStateToProps = state => ({
products: selectNetworkResource(state, PRODUCTS_REQUEST) || [],
});
We do this in orther to get rid of all the network related logic, in you example this:
- const booksReducer = (state = defaultState, action) => {
- switch (action.type) {
- case FETCH_BOOKS:
- return { ...defaultState, fetching: true };
- case FETCH_BOOKS_SUCCESS:
- case success(FETCH_BOOKS):
- return {
- ...defaultState,
- data: { ...action.payload.data },
- };
- case FETCH_BOOKS_ERROR:
- case error(FETCH_BOOKS):
- return { ...defaultState, error: true };
- default:
- return state;
- }
- };
As you see in the network reducer we don’t have an easy way to know which actions are SUCCESS, ERROR, ABORT…
- Is there an easy way to know that?
- Are you interested to have this ‘network history’ like this? If so I will be glad to submit a PR 😃
Issue Analytics
- State:
- Created 6 years ago
- Comments:26 (14 by maintainers)
Top GitHub Comments
If you want to preserve the data, you could just allow
requestReducer
to save data asdata => data
, (this would be the default, but we could make it as configurable by passingprocessData
callback to config ofrequestReducer
.In my example, only state for
FETCH_BOOKS
was kept in state. But you could easily trackCREATE_BOOK
as well by creating another reducer:And, if you wanted to keep track of all requests, you could use a helper I suggested in a previous post:
It depends on your use case. If you need it just for logging purposes, maybe it would be better to set some
redux-saga-request
interceptor? Or if you could need it later, then reducers are the best place to keep it. And withgetNetworkReducers
, you could easily achieve that.I like your approach, with my version of
requestReducer
, I just woudn’t need handleFETCH_BOOKS
cases, as this would be handled by therequestReducer
.Good point. Again, it’s why I preferred @poteirard’s initial approach. There, the reducer we’re passing to the HOR does not need to know of the outside world. It can continue performing it’s duties independent of any particular library which tries to expand it.
This also allows for better adoption of libraries which approach HORs in this way.