reactjsreduxredux-sagabatching

Batching actions in redux-saga


I read at https://react-redux.js.org/api/batch that one can use batch "to ensure that multiple actions dispatched outside of React only result in a single render update", like this (which I use in React event handler i.e.):

...
const submitData = event => {
    // ... some code before
    batch(() => {
        dispatch(first())
        dispatch(second())
    })
}
...

But my question is how to properly batch actions in redux-saga so the result is a single re-render instead of multiple, while dispatching multiple actions from a saga? I tried to put multiple put in all array but is not sure that it actually batches put:

yield all([
    put(addFirst({first: 1})),
    put(addAnother({second: 2)),
]);

The above approach is because I have read documentation at https://github.com/manaflair/redux-batch where the author wrote "Since this package got written, redux-saga improved and using all([ put(...), put(...) ]) seems to properly batch the two actions into a single subscription event, making redux-batch redundant in this scenario.", but in https://github.com/manaflair/redux-batch/issues/21 another person wrote that batching is handled by React unstable_batchedUpdates API. Still redux-toolkit (which I use) have an example with redux-batch as a store enhancer (https://github.com/reduxjs/redux-toolkit/blob/master/docs/api/configureStore.md).

So if anyone knows the correct way, please share your knowledge! Best Regards

EDIT: I only use batch in React event handlers to batch multiple actions. In redux-saga I want to use redux-batch package.

So the correct way (if using redux-batch which apparently redux-toolkit seems to like because example) is to:

import { reduxBatch }                            from '@manaflair/redux-batch';
import { put, takeEvery }                        from 'redux-saga/effects';
import createSagaMiddleware                      from 'redux-saga';
import { applyMiddleware, compose, createStore } from 'redux';

let sagaMiddleware = createSagaMiddleware();
let store = createStore(reducer, compose(reduxBatch, applyMiddleware(sagaMiddleware), reduxBatch));

sagaMiddleware.run(function* () {
  yield takeEvery(`*`, function* (action) {

    // Duplicate any event dispatched, and once again, store
    // listeners will only be fired after both actions have
    // been resolved/

    yield put([ action, action ]);

  });
});

Therefore to yield put([ action, action ]);, have an array in put and dispatch multiple actions?


Solution

  • I'm a Redux maintainer and author of Redux Toolkit.

    I actually just wrote a blog post entitled A Comparison of Redux Batching Techniques, which covers the various forms of batching available with Redux actions.

    As I just commented over in that redux-batch issue:

    The unstable_batchedUpdates() usage within React-Redux itself only attempts to minimize the nested re-renders from within a single dispatch. That does not overlap with the goal of this project, which attempts to allow dispatching multiple actions at once while only notifying store subscribers a single time.

    Per your original question, I'm not sure that you can use React-Redux's batch() API correctly in sagas, because of the need to yield put(). The batch() API (which is just React's own unstable_batchedUpdates() API, re-exported) relies on tracking any React state updates that are queued during a single event loop tick, and I suspect that use of generators may not work with that. I'd have to see examples in action to be sure.

    In any case, per the blog post, there's several options for batching, which work in different ways. It's all a question of what actual use case you're trying to solve.