Strongly typed and adaptable
We built an adaptable tool to such popular libraries like Redux and Zustand, while encouraging Typescript as much as we could
In-component saga control
Keeping in mind React components have their own lifecycle, sagas run on component mount and automatically cancelled upon its unmount
Cancellable network requests
We provide adapters for fetch and Axios that support cancellable network requests with the help of AbortController out of the box
Example usage
- 1. Dispatch an action
- 2. Initiate a side effect
- 3. Connect to the store
Suppose we have a UI to fetch some user data from a remote server when a button is clicked. For brevity, we'll just show the action triggering code.
import {useDispatch} from 'react-redux';
function Todos(props: Props) {
const dispatch = useDispatch();
const onSomeButtonClicked = () => {
const {userId} = props;
dispatch({type: 'USER_FETCH_REQUESTED', payload: {userId}});
};
...
}
The component dispatches a plain object action to the store. We'll create a Saga that watches for all USER_FETCH_REQUESTED actions and triggers an API call to fetch the user data.
import {createSaga} from 'promise-saga';
import Api from '...';
// Worker saga will be fired on USER_FETCH_REQUESTED actions
const fetchUser = createSaga(async function (action) {
try {
const user = await this.call(Api.fetchUser, action.payload.userId);
this.put({type: "USER_FETCH_SUCCEEDED", user});
} catch (err) {
this.put({type: "USER_FETCH_FAILED", message: err.message});
}
});
// Starts fetchUser on each dispatched USER_FETCH_REQUESTED action
// Allows concurrent fetches of user
const mySaga = createSaga(async function () {
this.takeEvery("USER_FETCH_REQUESTED", fetchUser);
});
To run our Saga, we have to connect it to the Redux store using the Promise Saga middleware.
import {configureStore} from '@reduxjs/toolkit';
import {createSagaMiddleware} from 'promise-saga/adapters/redux';
import reducer from './reducers';
import mySaga from './sagas';
// Create the saga middleware
const sagaMiddleware = createSagaMiddleware();
// Mount it to the Store
const store = configureStore({
reducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(sagaMiddleware),
});
// Then run the saga
mySaga();
// Render the application