Open mpeyper opened 6 years ago
The proposed solution breaks encapsulation on multiple occasions.
To start with, there is this action creator which belongs to the randomGif
component, but knows about an action from a different component (increment() from counter
):
export const getGif = () => (dispatch, getState, api) => {
dispatch(loading())
api.getRandomGifUrl()
.then((url) => dispatch(setGifSrc(url)))
.then(() => dispatch(increment()))
}
How can randomGif
hope to be publishable as an independent NPM package when it explicitly depends on counter
?
Then if I look at the counter
reducer, I see it reducing the TOGGLE
action from button
:
export default (state = initialState, action) => {
switch (action.type) {
case TOGGLE:
return { ...state, useMultiplier: !state.useMultiplier }
case INCREMENT: {
const increment = state.count >= 10 && state.useMultiplier ? 2 : 1
return { ...state, count: state.count + increment }
}
default:
return state
}
}
Again, it's dependent on another component and cannot be published independently.
Fixing it would start by removing dispatch(increment()))
from the randomGif
action creator and also remove the handling of TOGGLE
from the counter
reducer. Then you create a new saga which listens to:
TOGGLE
actions -> sets it's internal state to the corresponding value isEnabled = true/false;
SET_SRC
actions -> depending on the isEnabled
flag it dispatches 1 or 2 INCREMENT
actions.The saga is the glue that makes it all work. If you will, in DDD terms, it listens to events from two bounded contexts (button and randomGif) and converts them to a command for the third bounded context (counter). None of these bounded contexts know or depend on the others, nor should they. This saga would not be part of any component, or if you must place it in one, it would be part of the application component (or whichever parent contains these participating components). Of it could be part of a CounterWrapper HOC.
I have submitted a PR with a solution using redux-subspace.
Any feedback is welcome.