Reputation: 6977
Redux reducers should be without side-effects. But what if an action should trigger the download of a file in the browser where the content is based on the state of the store? Surely this should count as a side effect? Would something like the following be fine or should I be looking for alternative methods?
case 'SAVE_GRID': {
const { json } = state
fileDownload(json, 'data.json', 'application/json')
return state
}
Upvotes: 8
Views: 10719
Reputation: 2989
I use the hook useReducer in lieu of redux. I'm not a fan of putting side effects in action creators - then it is unclear what the purpose of the action creator is: build an action, or run side effects?
I found a library here that fulfills my purpose:
https://github.com/Jibbedi/use-reducer-effect
Upvotes: 0
Reputation: 134
Reducers can drive effects, in fact they do it all the time via state changes which trigger UI rendering — arguably the application's most important effect.
Driving non-visual effects isn't as common but there are at least two solutions: redux-loop and redux-agent (which I wrote).
From the respective sites:
redux-loop:
A port of the Elm Architecture to Redux that allows you to sequence your effects naturally and purely by returning them from your reducers. https://redux-loop.js.org/
redux-agent:
Redux Agent extends React’s model to non-visual I/O: describe a network request, a storage operation, a websocket message, … and let the machine worry about performing it. Logic stays in the reducer, components stay lightweight, and it’s easy to see what state triggers which effect. https://redux-agent.org/
(The quotes also hint at a major difference between the two: redux-loop returns effect descriptions from the reducer, thereby changing its API and requiring a store enhancer. redux-agent works with vanilla Redux API.)
Upvotes: 0
Reputation: 67306
Unless you have very complex state transitions, the actual fileDownload
should happen in an action creator, not in the reducer. The reducer should be responsible for merging/reducing state and that is all.
action:
export const saveGrid = (json) => {
return (dispatch) => {
fileDownload(json, 'data.json', 'application/json')
.then(() => {
dispatch({ type: 'SAVE_GRID', json });
});
}
}
reducer:
case 'SAVE_GRID': {
return {
...state,
json: action.json
}
}
Upvotes: 8
Reputation: 5452
There are libraries to handle such "side" effects.
For example:
https://redux-observable.js.org/docs/basics/Epics.html
Upvotes: 1