Open mattiamanzati opened 9 years ago
I would still say side-effects should somehow be part of the domain. Therefore keep them in reducer and reducing them seems most natural to me. https://github.com/staltz/flux-challenge/blob/master/submissions/salsita/src/reducers/sithLoadingReducer.js#L110-L122
Elm has a beautiful solution for this, reducers can return either a new state or a new state and a side effect, i think it's a solution redux should embrace
Yeah, I saw that and I loved it! Maybe there's a way to implement it over a store enancher? Handling side effects in redux atm is kinda annoying :/
@mattiamanzati check the repo above, it's exactly what I have implemented https://github.com/staltz/flux-challenge/blob/master/submissions/salsita/src/components/Root.jsx#L15-L22.
bingo! That's pretty much what i meant
There is also an article, describing the concept http://blog.javascripting.com/2015/08/12/reduce-your-side-effects/
Yeah, but should'nt side effects being written 1:1 per reducer? I mean, the reducer itself should return the new state along the side effects to trigger, right? I have not understand correctly how you enqueue side effects, since each reducer can change only it's state branch, and not the effects key.
In your example side effects lives in a different reducer, and this is fine as long this works, not so much in terms of readability, right? Maybe I am just missing the whole point.
In my example, I am not limiting effects list to particular reducer, they are global in the reduction, independent of any reducer.
Oh, now after a bit of testing and reproducing your code I got it. The only question is: is ther a reason to store to keep the effects queue in the store, or it is just a way to have a global variable? I have implemented it via a store enancher, do you think that this implementation can have problems or lacks of purity over time? :/ Note that I have decided to dispatch actions as a side effect handlers.
/*
// inside your reducer you can do this
return action.withEffect(authUser, (effects) => effects.concat([{
type: REDIRECT,
url: '/user/dashboard'
}]));
create the store with
compose(
sideEffectHandler
)(createStore);
*/
// the store enancher which handles the reset of $effects
export default next => (reducer, initialState) => {
// create the finalized store
var store;
// enanche the reducer you provide
var enanchedReducer = (state, action) => {
// array to store side effects generators in
var sideEffectsStorage = [];
// attach on action a way to add a new side effect (found no better way :( )
action = {
...action,
// append the effects to the side effects store
withEffect(state, ...effects){
sideEffectsStorage.concat(effects);
return state;
}
};
// run the user reducer
const result = reducer(state, action);
// flatten the list of side effects
sideEffectsStorage = sideEffectsStorage.reduce((effects, effectGenerator) => effectGenerator(effects), []);
// now that side effects have been collected, trigger them!
sideEffectsStorage.forEach(effectAction => store.dispatch(effectAction));
// return the regular result
return result;
};
// make the enanched store
store = next(enanchedReducer, initialState);
return store;
};
Flux/Redux are inspired by event-sourcing / CQRS in the backend which exist for many years.
In the backend there's a concept we use for this kind of problem that has not yet really shipped to the frontend world: Saga.
You can read an example I gave here on how this could be used in Redux: http://stackoverflow.com/a/33829400/82609
A presentation talk (backend): https://www.youtube.com/watch?v=xDuwrtwYHu8
You might also find useful https://github.com/salsita/redux-side-effects, what is basically Elm like side effects reducing yet with a bit of syntax sugar.
(Yes, I am author)
@tomkis1 you might want to join this discussion: https://github.com/paldepind/functional-frontend-architecture/issues/20
Well, I've tried many methods, mostly about routing side effects, like reducers, middleware and store enancher. How do you handle them? What kind of side effects have you faced in your experience?
I am wondering about them because I'd really want to handle them correctly to add a "remote desktop" feature inside my app, in order to provide customer support.