Closed Zacqary closed 8 years ago
It sounds like what you're really looking for is the ability to return something like the StartApp style (Model, Effects Action)
in redux, yes?
I recently proposed an alternative approach here: https://github.com/rackt/redux/issues/1182. I think it addresses your concerns about putting this logic into the view.
I'd like to make it a library, but that's not yet a thing. But perhaps the example I showed there will give you some ideas.
@winstonewert So let me see if I understand how this would work with reactions:
SET_METRIC
action triggers the reducer for context
.context
reducer checks to see if state
contains a key corresponding to the action's metric
. If it doesn't, it sets that key's value to something like ...
.context
reactor checks to see if any keys have values of ...
, and if they do, it sends an XHR to find out what their values should be.SET_CONTEXT
action to update the store.Is that about the gist of it? I noticed your implementation is actually dispatching an action to a doReactions
function, so there's an extra step in there — I guess if you don't assume that all reactions necessarily trigger an XHR, then the extra step of doReactions
isn't necessary?
I saw redux-saga
mentioned in your thread too. It seems like it's more complicated but maybe more bulletproof? Gotta look into that more later.
I guess if you don't assume that all reactions necessarily trigger an XHR, then the extra step of doReactions isn't necessary?
Actually, doReactions is pretty key to what's going on. doReactions keeps track of the pending requests. That's what prevents the system from starting the same request over and over again. That lets the reactions function simply worry about which data is missing right now, without being concerned about whether or not a request has been made for it.
The context reducer checks to see if state contains a key corresponding to the action's metric. If it doesn't, it sets that key's value to something like .... The context reactor checks to see if any keys have values of ..., and if they do, it sends an XHR to find out what their values should be.
You could. Or you could simply if check if state.context[state.metric] is undefined.
I saw redux-saga mentioned in your thread too. It seems like it's more complicated but maybe more bulletproof? Gotta look into that more later.
redux-saga has the benefit of actually being available as a library right now. I wouldn't think it was more bulletproof, but its more complicated and allows more complex interactions then my approach.
I should note you can do something simpler then me, I was trying to be more generic. You could probably do something like:
var contextRequests = {};
store.subscribe(() => {
var state = store.getState();
if(!state.context[state.metric] && !contextRequests[state.metric]) {
contextRequests[state.metric] = fetch('/api/context/' + state.metric')
.then((result) => store.dispatch({type: 'SET_CONTEXT', metric: state.metric, context: result});
}
});
You could. Or you could simply if check if state.context[state.metric] is undefined.
I could if I were passing the entire state
object. I'm approaching this from the line of thinking that reducers are only supposed to receive the property of state
that they act on, though. My assumption is that each reducer with a corresponding reactor of the same name will check the result of its reducer, and then trigger a side effect if need be.
Maybe that's a little limiting. I guess it makes more sense to pass in the entire state
and destructure it.
This is why I was thinking of a reducer version of thunk. Since the context
reducer can handle an action that's supposed to set metric
, it gets the necessary value without having access to other parts of state
.
In my view, you shouldn't be managing the different properties in different reducers. Splitting it up into different reducers is intended for when you have independent pieces of state. In this case, those pieces of state aren't independent.
I think we have too many discussion threads about this. Please use any of the existing ones. (You can search for "side effects".)
Forgive me if this has already been figured out; I've looked through the Redux docs and at some middleware and I haven't found a solution myself.
Here's the problem I've had to solve:
metric
.metric
, and populate a selection box with themetric
's correspondingcontext
options.context
options need to be fetched asynchronously from the server. If we haven't cached thecontext
s for thismetric
yet, request them. Meanwhile, render a "loading" symbol in the selection box.context
s have been fetchedIn Elm, this would be a simple act of making
context
aSignal
. Since Redux recommends Elm-like architecture, I'd like to replicate that pattern.I have a solution that I'm a little uncomfortable with. After the
metric
gets updated, it gets passed to the React view. The view sees that the store has nocontext
for thatmetric
yet, and then calls an action creator calledrequestContext
. This usesredux-thunk
to asynchronously fetch thecontext
s, and then dispatches an action to add thosecontext
s to a lookup table in the store. After that, the view is re-rendered, and it can now look upstate.context[state.metric]
.But the thunk first has to dispatch an action to set those
context
s to a value indicating that they're loading. Otherwise the view could keep requesting thecontext
s over and over again if it re-renders before they're fetched from the server.So that's the problem: I'd much rather be able to keep that action dispatching/updating logic out of the view. I just want to pass a value to the view, and have it display that value, whether it's a loading state or retrieved data.
I'm not sure if
redux-promise
is an effective solution for this either — it seems to be about delaying the dispatch of an action just likeredux-thunk
, but with a different syntax.I suppose the better solution would be for the reducer handling
context
to make the request, return a loading state, and then return the result of the request. But that would mean having to dispatch an action from the reducer, right? I don't think that's possible without middleware — maybe something like thunk (which exposesstore.dispatch
), but for reducers instead of actions?