I seem to have encountered a situation where I cannot avoid the dispatch-within-a-dispatch problem in Flux.
I've read a few similar questions regarding this problem but none of them seem to have a good solution besides setTimeout hacks, which I would like to avoid.
I'm actually using alt.js instead of Flux but I think the concepts are the same.
Scenario
Imagine a component that initially renders a login form. When a user logs in, this triggers an XHR that eventually responds with authentication information (eg. the user name), and then fetches some secure data based on the authentication information and renders it instead of the login form.
The problem I have is when I attempt to fire an action to fetch data based on the XHR response, it is still in the dispatch of the LOGIN_RESPONSE action, and triggers the dreaded
Error: Invariant Violation: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.
Example
I have created this jsfiddle to demonstrate the problem.
I have a Wrapper component which either renders a login button or a Contents child component, based on whether the user is set in MyStore.
First, the login button is rendered in Wrapper component.
Clicking the button dispatches the LOGIN action.
After a delay, the LOGIN_RESPONSE action is dispatched (via the async mechanism in alt.js).
This action triggers MyStore to update the user name.
Wrapper component observes the store change and updates its state.
This causes Wrapper to render Content component instead of the login button.
Content component, on mount, attempts to dispatch the FETCH_DATA action, which fails because the dispatcher is still dispatching LOGIN_RESPONSE. (If I wrap the FETCH_DATA dispatch in a setTimeout it works, but it feels like a hack).
Variations of this seems to be a common scenario. In fact almost all the related questions have a similar scenario, but with no good or concrete answers.
React - Authentication process : Cannot dispatch in the middle of a dispatch
Dispatching cascading/dependent async requests in Flux/React
Flux Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch
Is there something intrinsically wrong with this data flow? What is the proper Flux way of doing something like this?
This is a common problem with dispatching in componentDidMount in many libraries. The solution is to wrap dispatches in React's batched updates; luckily, Alt allows you to do this with the batchingFunction option:
var alt = new Alt({
// React.addons.batchedUpdates is deprecated:
// batchingFunction: React.addons.batchedUpdates
// use this instead in newer versions of React
// see https://discuss.reactjs.org/t/any-plan-for-reactdom-unstable-batchedupdates/1978
batchingFunction: ReactDOM.unstable_batchedUpdates
});
See https://jsfiddle.net/BinaryMuse/qftyfjgy/ for a working example and this Fluxxor issue for a description of the same problem in a different framework.
I believe our loyal friend, the Dispatcher, has its right to complain.
I will try to describe a hypothetical situation before throwing my conclusions. Let's say an app has two stores S1 and S2 and two kinds of actions A1 and A2. The right flow of an usual Flux implementation should be something like:
Component fires an action A1 (basically a dispatch);
Single dispatcher distribute correspondent payload to all registered stores;
S1 consumes the payload and maybe updates its state;
All components listening to changes in S1 check for changes they are interested in and maybe update their internal states (possibly triggering an re-render);
S2 consumes ... (like in step 3)
All components listening to changes in S2... (like in step 4)
Now all stores are done dealing with the action payload, components can fire new actions (A1 or A2).
One of the greatest advantages of using Flux over traditional MVC is that Flux gives you The Gift of Predictability. This feeling empowers the developer in such a way that they believe that, by correctly applying the Flux philosophy, they are sure that the order of execution is always somewhat similar to:
A1 > S1 > S2 > A2 > S1 > S2 > ...
This is a really great deal, especially when trying to find sources of bugs. One could argue that enforcing a predictable chain of events can lead to some inefficiency and he is probably right, especially when dealing with async calls, but that is the price you pay for having such a great power!
Due to the async calls, things can get a little messy. Something like the following chain of events could happen:
A1 > S1 > A2 > S2 > S1 > S2 > ...
Maybe your app can handle such chain of events quite well, but such "unpredictability" hurts basic motivations behind Flux's unidirectional data flow.
I feel there is no consensus in the community on how to handle such situations, but I will share my general solution: "for the sake of predictability, make sure to not trigger any new actions before you have the last one totally processed".
One way of doing this is by downloading all the necessary (new) data the app needs to re-render successfully before triggering any additional actions. In your example, this could be achieved by first downloading the data involved in the LOGIN_RESPONSE and FETCH_DATA actions and wrapping it in a single payload and then dispatch it, so all the components will have the data they want already in the stores without asking for more.
Related
Noob here, just learning React , React Redux and Redux Saga.
I stumbled upon a particular scenario.
Consider:
I have a ToDo app, obviously this app is composed of various components.
There is a specific state that is purely for a specific component only. (this will be the kicker)
Ex. mode in this case. Basically it determines how to display a specific todo item ( edit mode or normal mode ).
Now I am using redux saga to take care all of my codes that will cause side effects ( ajax processes in this case ).
Now if i click the edit button of a list item, i set that specific list item mode state to edit, which changes the appearance of the list item to text box to allow editing the list item title.
It will also display a new buttons , save button to save the changes and cancel button to cancel the changes and revert to normal mode.
After editing the title, i can click the save button, when I click the save button, it will fire an action which my root saga listens ( EDIT_TODO )
my root saga then picks up the action and routes to the appropriate worker saga to do the actual editing of the todo item ( involves ajax request )
now this is where the culprit is
I want to do a specific action only if the ajax operation is successful ( if editing is successful done via ajax ) and that is change the state of the list item to normal from edit.
how do I do that inside the saga?
There are 2 ways actually.
1.) Make all your app state as a state on your redux store so you can just create actions in which your sagas (and other components) can just fire an action and your component will act on it if it is changed.
This approach is ok only to a certain degree, yes there might be a state that is generaly shared across your components, but there are also state that is very specific only to a specific component.
On those cases it does not make sense to just add that very specific state to the redux store just so the sagas can fire an action for it.
2.) Include a function call back on the action payload.
So in my ex. when save button is clicked, when i fire the appropriate action that my root saga is listening, i will include a callback that will set the list item to normal mode.
Then inside the saga that is actually doing the editing of the todo item, when the ajax operation is good, call that callback.
To clarify, the callback that I passed along with the action payload, is a callback that only alter that local state of that specific component ( mode )
All global states ( registered on redux store ) are all altered via the reducers only.
For me, mentally it doesn't feel right? I don't know but sagas are just generators right and the purpose of generators in the context of asynchronous programming is to make asynchronous looking code to make it look synchronous to some degree (very basic analogy of generators there) and doing this callback thing dont feel right to me? or doesn't fit well with this approach? or kind of like breaks the purpose of generators ( sagas )?
I know it might help me to think that , that callback is just part of the data or payload of that action and not to think about it as a callback, but still doesn't feel right for me at least.
Or am I just being picky here?
So is there an alternative to this aside from the above?
I guess my question is,
Is there , a way to make a component listen to an action? then if that action is fired wherever from any component or sagas, that action will be picked up by that specific component then that specific component will alter its specific state in return?
Im not sure if that question make sense, or that is the proper way to phrase it. Im just a newbie to the subject so please go easy on me.
Thanks in advace.
One of the really cool things of sagas is that you can either hook many sagas to one action but then you can also dispatch other actions from one saga. Let's say your EDIT_TODO (I'd rename this to UPDATE_TODO and use EDIT_TODO when kicking the edit mode) action fires the update saga below.
export function* update(api, action) {
const { item } = action;
const response = yield call(api.update, item)
if (response.data) {
// Dispatches action for success EDIT_TODO_SUCCESS
yield put(updateSuccededActionCreator(item));
} else {
const error = response.error;
// Dispatches action for success EDIT_TODO_FAILURE
yield put(updateFailedActionCreator(item));
}
}
Another thing to clarify, you can use the combineReducers redux api to "merge" different slices into one store. So you can design your store in the way you want, and a slice can just hold data for a specific part of your app. Something like:
const reducer = combineReducers({
todoState: todoReducer,
sessionState: sessionReducer
});
Im a little uncertain as to how Redux ties in with React ( without using the ReactRedux library ). Assume the following component structure
App
--TodoListController
----SomeComponent1
----TodoList
------TodoItem
--ProfileController
Question 1. Which components should listen for changes?:
Im assuming that the proper component to subscribe for state changes in the redux main (and only ) store should be the TodoListController and the ProfileController respectively ( essentially the non presentation components ).
Is this correct to assume or should all components listen to the state and render whatever is of interest to them? I essentially dont know which component should listen to state changes and am only guessing at this point
Question 2. Handling network calls:
I know this is to be examined per case but ill mention it anyway. Currently im handling network calls in the following manner:
A) When TodoListController mounts i get the state from the mainstore and also initiate a request to the server for the latest data. I also listen for changes in the store. So in practice:
class TodoListController extends React.Component{
componentWillMount(){
mainStore.subscribe()
getDataFromServer(function(data){
mainStore.dispatch(data)
})
}
getDataFromStoreAndUpdate(){
this.state.datawecarefor = mainStore.todoReducer.data
//set the state here to trigger a rerender
}
componentWillUnmount(){
mainStore.unsubscribe()
}
render(){
//render whatever component here that uses this.state.datawecarefor
}
}
Do you see any obvious flaws with this approach? I dont know what i dont know at this point.
Question 3. Where should store related helper functions live?
I currently have a reducer that holds all todolists for various users. Currently, when the redux store state updates i retrieve all this data and iterate through it to find the user im interested in. This shouldnt be in the controller itself but as a hepler function. I thought of creating a wrapper around the Redux store and it's reducers to create functions like getTodoListForUser(userId) but i dont know if thats the right approach. How do you handle logic like that?
P.S: Many people will point out that i should use the ReactRedux library that comes with many optimisations and they re probably right. This however isnt a production project, only one im trying to put together to better udnerstanding the core of both these two libraries before moving to something more optimal.
I know you don't want to use ReactRedux, but luckily enough there is a video of Dan Abramov explaining the source code. You should watch it, it will explain why they did what they did and how they did it. When I was first learning how redux and react worked together it made every so much more clear (and then I used ReactRedux anyway :)).
https://www.youtube.com/watch?v=VJ38wSFbM3A
There has been a lot of debate on where to connect React App's to the redux store. But it's mostly recommended that you want to connect where it makes logical sense. For example, if you have a container component that holds a bunch of comments, you don't need to connect all of the comments, you can just connect the container. In the same light you don't just want to connect your entire app at the top because then its more expensive to diff and update your app.
On another note you should probably try to handle network calls in redux middleware and dispatch an action your react component catches to cause a render.
I'm writing a react/redux app that has a Job object that fires Events from time to time. On these Events the user is expected to react. I can easily write the React component that shows such a job or events, but don't know where to put the logic when
the user is not on the Job page
and an Event happens
and I would like to pop up a notification
Of course, once I get access to the store, I can create the notification too.
Shall I handle such logic in a React container that render() nothing?
Or handle it with vanilla JS when I load a Job data?
Or to handle it in the reducer when the data is stored? (probably not recommended)
My problem with the option two is that I don't know how to access the redux store from a totally ad-hoc code, as my store is not a global object.
What do you recommend?
Generally speaking, the two places that you'd put code that needs to access the store (for either dispatching or receiving state updates) is in middleware or a connected component. If you specifically want to respond to an action coming through, then you would need to use middleware.
I actually threw together a sample timer middleware in a chat discussion a few weeks ago. I just pasted it as a gist here as an example: https://gist.github.com/markerikson/ca96a82d6fdb29388aca4052a9455431 . Illustrates the idea of doing something asynchronously in the background.
You may also want to look through the list of middlewares in my Redux addons catalog: https://github.com/markerikson/redux-ecosystem-links/blob/master/middleware.md . Numerous examples of ways to execute background-ish logic.
Finally, https://github.com/yelouafi/redux-saga (which is itself a middleware) is a fairly common lib used to do background-ish logic.
I have a few ajax requests that are not directly manipulating my apps state. In a react/redux application is it necessary (or is there any benefit) to dispatch an action for these ajax requests instead of just sending an ajax request directly in the component?
To simplify my scenario, I essentially have a list of objects on my redux state. I am using a form to post a new object to the database, upon successful post I am redirecting to the list page where a GET request is sent and the list is fetched and the state is updated.
The AJAX call to post a new object is not directly manipulating my state.
The team I am working with is going through the full 3 step redux async steps
ex: 'FETCH_REQUESTED', 'FETCH_SUCCESS', 'FETCH_FAIL' along with the respective reducers for all the AJAX requests and it's a big hassle to add more and the reducers don't seem to make sense.
You can absolutely send AJAX calls directly from components!
Redux is a tool for making shared state globally available to multiple components, and changed in predictable way. In any case where you don’t find this necessary, don’t do it.
Keeping AJAX calls in action creators is convenient when different components make the same API requests and then change the state in similar ways. It is also convenient if you want to avoid firing off a request when there is already some cached data available, and you want to keep such checks in a single place rather than scattered across the components.
That said Redux is only concerned with how global state is updated, and if you just need to make an AJAX request from some component, you don’t have to write an action creator or a reducer for it unless you find it convenient.
Generally saying Redux (and Flux) is what you might consider refactoring your code to when you have many complicated components; not what you should start every component with. You can use only the parts of it that you need (e.g. just the synchronous stuff), or even avoid it altogether in some cases (e.g. a collapsible panel doesn’t have to store its state in a store). Only use it when you understand the specific benefits it gives you in a particular situation, never “just in case” or because it is popular.
See also my answer to “How do dispatch a Redux action with a timeout?”
To address your specific example, you might want to use Redux for this if you use the benefits Redux gives you: maybe you dispatch an action to update the form optimistically and display the new list right away, and merge it with the fetched list when it is available so that the interaction appears instantaneous. That is the use case for async action creators. If you’re not looking at this kind of UX complexity, I’m not sure Redux is necessary at all.
Try using below link
https://github.com/sskyy/redux-task
It can help you to manage AJAX request state without those verbose actions and reducers.
I am using flux architecture with React and I have an issue I don't know how to handle.
I need to write a logic that will listen to dispatched action (like a store) and will trigger an action in case the payload contains a specific value.
For example, assume there is an action that dispatch "ITEM_CREATED". My component should catch this "ITEM_CREATED" event and it's payload, and check whether the payload contains correct serial number. In case the serial number is incorrect, my component should execute an action.
Implementing this logic in a store will lead to a-synchronic store, moreover, in flux I can't trigger actions from store.
A possible solution is to create a "virtual" component (with falsy render() method) that will do that logic. Such solution will force me to put this virtual component in my JSX markup, which seams like a hack or a bad workaround.
I really want to know what is the flux solution for such scenario.
Thanks
The answer here is to back up and do everything in response to the original action, not to create a cascade of actions.
See also:
Flux Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch
Dispatching further actions when handling actions
https://github.com/facebook/flux/issues/133#issuecomment-70775063