Write your store in one place, without boilerplates
- Easy to implement on your current redux + redux-saga architecture
// ./store/counter.ts
import {delay, put} from 'redux-saga/effects';
import {makeResource} from 'saga-resource';
interface CounterState {
count: number;
}
interface CounterReducers {
inc: (num?:number) => CounterState;
}
interface CounterEffects {
asyncInc: (num?:number) => any;
}
const counter = makeResource<CounterState, CounterReducers, CounterEffects>({
name: 'counter',
state: {
count: 0,
},
reducers: {
inc: (num=1, {state}) => {
return {...state, ...{count: state.count + num}};
},
},
effects: {
*asyncInc(num=1): any {
yield delay(500);
yield put(counter.actions.inc(num));
},
},
});
export default counter;
// ./store/index.ts
import {combineResources} from 'saga-resource'
import counter from './counter'
const combinedResources = combineResources({counter})
const rootReducer = combinedResources.combineReducers()
const rootSaga = combinedResources.getSaga()
export type AppState = ReturnType<typeof rootReducer>
const sagaMiddleware = createSagaMiddleware();
const store = createStore(rootReducer, bindMiddleware(sagaMiddleware))
sagaMiddleware.run(rootSaga)
export default store
You are all setup, let's dispatch resource actions from your component.
// ./YourComponent.jsx
import counter from 'store/counter';
// ...
const mapDispatchToProps = (dispatch) => {
return {
add5: dispatch(counter.actions.inc(5))
asyncAdd: dispatch(counter.actions.asyncInc())
}
}
// ...
actions will be generated by reducers and effects you defined.
yield saga-resource effect from your saga file.
// ./YourSagaFile.js
import counter from 'store/counter';
// ...
function* asyncAdd5(){
try {
yield counter.effects.asyncInc(5)
}
}
// ...
There are 3 built in reducers, set, update, clear
set
will overwrite your state e.g. dispatch(resource.actions.set(newState))
update
will set the key with the payload, the key can be a path, e.g. update your key 'a.b.c with data' dispatch(resource.actions.update('a.b.c', data))
clear
clear will reset your state to it's initial state. e.g. dispatch(resource.actions.clear())
There are 4 built-in effects, createRequest, updateRequest, fetchRequest, deleteRequest
, these four effects will send request corresponds to post, patch, get, delete
, with an exception that fetchRequest
will set your state based on data the request returns.
You can call these methods only if you have a path
setup in your resource definition.
For example:
// ./store/user.ts
import {makeResource} from 'saga-resource';
export interface UserState {
username: string;
}
const user = makeResource<UserState, {}, {}>({
name: 'user',
path: 'http://localhost:8080/user',
state: {
username: 'Uninitialized user',
},
});
export default user;
Fetch user by dispatch(user.actions.fetchRequest())
Or you can use effect directly in you saga file yield user.effects.fetchRequest()
All effects will have loading status stored in resourceState.meta.loading
When ever resource effects throws an error, { type: SagaResource.actionTypes.ERROR
, data } will be dispatched.