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
Related
I am triggering a function in saga with Dispatch. Can Saga send me information inside the component after it's done all its work? Actually, I will explain what I want to do with a simple example, don't laugh, please, I am sure this won't work.
dispatch({
type: 'triggerApiCallInSaga'
}).then(res => doSomething(res.payload));
I want to make changes in the component as a result of the query I have made. I actually want to maintain this with redux, but I'm worried about my state expanding. Also it seems silly to me to check via redux to close the modal.
In short I want to receive signal from saga and see it in component. Can I make a listener for it? Or does saga have a method for this? Although I searched, I could not find the right results. redux-sagas callback (aka sagas and setState) I encountered a similar question to my question, but here I encountered a negative answer that I did not understand.
Thank you.
The answer you linked is right, the expected way for a saga to communicate back to component is through updating redux store. Adding promises/callbacks is an antipattern, because each action can be handled by 0-n sagas - of course you might know in your case it is just one saga, but the framework doesn't guarantee that in any way and so it can lead to some unexpected issues later on.
That said, even though it is an antipattern doesn't mean you can't do it.
The easier way is to add an callback to the action, so e.g.
// component
const callback = data => console.log(data)
dispatch({type: MY_ACTION, callback})
// saga
yield takeEvery(MY_ACTION, function*(action) {
action.callback({foo: 'bar'});
})
If you would prefer promises, that is something that can't be done easily with just the saga library, but there are packages that can add this functionality like:
https://github.com/adobe/redux-saga-promise
Ideal solution to this problem would be to run a saga on top of a useReducer hook, so that instead of adding component specific state to redux store you would just work with a component state. There are some attempts out there that try to do this, but unfortunately react is still missing some functionality that would allow us to reliably do this without introducing other issues.
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
});
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.
Say I have an action someAction(params) that takes params which is managed in a store paramsStore:
paramsStore.listen(function(params) {
someAction(params)
})
It seems that I can't just call this in my view because apparently this goes against the Flux way of doing things (actions shouldn't be called within store listeners).
The reason I have someAction inside the store listener, is because I want it to be called every time the paramsStore is modified. How can I achieved this without resorting to the 'unpattern' of calling actions within stores listener?
The right "flux way" of doing it would be to call the someAction(params) wherever information is dispatched to paramsStore.
Understanding what someAction does will give more clarity. Does it really need to be an action? If you're just doing some manipulation in the store data, you could have it as a local method in the paramStore.
While I am new to flux as well I could offer a suggestion. State that is needed to determine the outcome of an action that is held by Store A could be attached to a get method. This state can be retrieved by a View with a getter. When the action is called this state can be sent as a parameter. If something needs to be async it can now be done here (ajax call or something else) based on what the state is. Either the result of this or a promise object can then be used to trigger an action which is passed to the dispatcher. The dispatcher sends the result or promise to the store. The store then updates its state and the process repeats as necessary (when initial action is triggered).
I think a little more detail of what exactly you need would help actually. I do believe listening for for an action and triggering another action inside the store doesn't coincide with flux. I do think there is likely a way to accomplish the actual result you want using flux but without more detail this is the best I could come up with. Also, in reality you can implement anything you want. Flux is just a model and by extension a self imposed constraint to help with structure.
If you are using Flux as is, you could refer to the original image of the whole architecture at https://github.com/facebook/flux.
As you can see not only views could create actions. There are also Web API Utils which could create ones. Generally speaking not only API utils can do this. It's totally okey to create actions in order to start some behaviour according to outside world, some services or something else.
But in your case you are trying to create an action on some store update listener. As far as I can understand this would result in some changes in one or few other stores. In this case you probably don't need to create an action in the listener, but rather create some relations between your stores with waitFor API. Here is a link with detailed information: http://facebook.github.io/flux/docs/todo-list.html#adding-dependency-management-to-the-dispatcher.
What is the advantage of using Flux over a global event bus? I think the dispatcher is all that is needed:
component publishes 'user event' with data to the dispatcher
dispatcher executes handler of the subscribed store
handler publishes 'update event' with the store's updated properties
dispatcher executes handler of the subscribed component, and updates component state with the store's updated properties
What am I missing here that I can't do without Flux?
I think what others have said about application structure and the change event is important, but I should add this one thing:
The dispatcher's waitFor method is the biggest difference between registering the stores with a dispatcher vs. the stores listening to a global event bus. This method lets you manage which stores update before others. And that becomes vital when you want StoreB to look first at what StoreA did before it decides what to do.
You could think of the dispatcher as a global event bus with a waitFor method, and that would be somewhat accurate.
I'm not an expert in flux but an architecture doesn't enable you to do something that wasn't possible before, it gives your application a structure that is extensible and understandable.
I believe it's all about code structure which is understandable even in large scale.
Supose you have appState which holds underlying data for components.
The components call action. Action is responsible for gather data from XHR or modify the incoming data from component and then it dispatch complete data to subscribed store.
Store is the only part of your code, which can modify your appState and it is basically the only thing, what it does. It takes data from action and store them to appState or removes some data from appState according to action.
Then you fire stateChanged event, which your component should listen to and will rerender.
So you have all action specific logic in actions. You handle appState only in stores. And that should help you keep your code understandable.
Flux pattern
My understanding of why is good idea to dispatch only complete data comes mainly from this article. And it is based on official Facebook Flux diagram
The advantages of this approach are:
stores are simple and synchronous, does not contain decision logic, just handles given data
there is no need to fire another action in store, which will break one-directional chain of Flux
dispatcher is the single channel for all state changes, it knows what action with what data is processed, so its easier for debugging
You basically described flux, the only difference is:
stores emit a change event
And the component updating its state isn't part of flux, that's a common practice for integrating flux and react.
Flux just names each of these pieces and gives guidelines on what each piece's responsibility is.
It's essentially a main event emitter (dispatcher), the event types (actions), functions that emit an event on the dispatcher (action creators; the event body is a payload), and other event emitters that: keep state, listen to the dispatcher and emit change events (stores).
At least that's how it works in JS. The core principle is the unidirectional data flow. There are plenty of event emitters that are used for bidirectional communication.