NGXS multiple sub states update from same async action - ngxs

I have a parent-child states that manage reports and documents respectively.
When a report is submitted, I need to store the user posted data for the report and the async action returned id in the parent state, and the async action returned document in the document state.
Since I need the async action returned data in two different state actions, is there anyway to acomplish this without dispatching another action from the parent?
In React apps with redux toolkit different state slices can listen to pending/fulfilled/rejected states for the same async thunk so I was wondering if there is something similar for NGXS.
Thanks.
I know I can dispatch actions from another action providing different payloads so other sub states can listen to them and update accordingly, but I was hoping to accomplish that in one action.
I've also thought about calling the http async action first and after getting the result dispatch the store action but not sure if this is an anti-pattern.

Related

Get data from promise in redux

When Reducer update state and I get state from store it returns a promise. Inside promise in values I have my data How can I access this value in promise.
enter image description here
To use async actions with Redux, you are going to want to use a middleware, mostly either redux-thunk or redux-saga. See Why do we need middleware for async flow in Redux?
As a beginner at Redux you should probably start with the official documentation on the topic.

Merge two distributed asynchronous tasks results and trigger an action

I have the following complex scenario and looking forward for the best solution:
- The first async process calls a third-party service and gets an Id
- The second async process gets an attribute from a client and tries two update a record in database based on the Id from the first part and the new attribute.
Also I would mention that the both above async tasks are triggered inside different APIs(spring boot based restful APIS) and they are not inside the same method. In other words, it is a kinda of problem in a distributed system.
The silliest solution would be having a loop to wait for the first async task result and complete the whole process.
Any suggestion?

React-Admin: How can we dispatch a store action from within the `authProvider` code?

In the async fetch of the AUTH_LOGIN we want to dispatch an action after we fetch data from additional endpoints (apart from the login endpoint, for example we call another endpoint to bring data for the account, etc.).
We have all in place (action, reducer, used the combineReducers, etc.), but we are not sure how we can "connect" the authClient function in the authClient.js file, as this is not a component in order to use the connect function.
How could we dispatch an action then?
As you can see in the authentication side effect, the data returned by your authClient will be set as the payload of the USER_LOGIN_SUCCESS action which will be dispatched automatically when the authClient resolve.
To apply any logic to the authentication payloa and eventually dispatch some custom actions, you'll have to create a custom saga which will react to USER_LOGIN_SUCCESS actions.
We should definitely add an example in the documentation

Calling UI action from store

Short version of the question:
Can I fire UI actions from the store?
Long version of the question:
I'm writing food delivery app with reflux. It's seems like I'm not quite
understand how actions should go in my applications.
I have BasketStore, StatusOverlay (component) and actions:
// BasketStore actions
basketSync
basketSync.Completed
basketSync.Invalid
basketSync.Failed
// StatusOverlay actions
statusOverlayOpen
statusOverlayClose
The application works the following way:
I press button and send basketSync action. Once it happened the overlay is starting to be shown and BasketStore sends request for the data to the server.
Then accordingly to server response I fire basketSync.completed, basketSync.failed, basketSync.invalid. When it invalid or completed I close overlay, otherwise I show another overlay.
The question is how should I manage actions? Should I listen to basketSync inside of StatusOverlay to open it and close it on basketSync.completed, basketSync.invalid or it will be better to listen to just statusOverlayOpen, statusOverlayClose and fire these actions somewhere inside of BasketStore.
Short answer: In the standard Flux architecture, Flux stores should only emit a simple CHANGE event, so, no, you can't fire UI actions from the store.
I've seen stores triggering actions in many Flux applications. Some people like to put their async work inside the action creators and trigger "completed" and "failed" "sub-actions" inside the callbacks of the async work, others put the async work within the store itself and in that case trigger the sub-actions from there. They key point, as I've seen it pointed out, is that in the callbacks you should trigger a new action, rather than update state directly, so that data mutations only ever happens as an effect of actions, not async callbacks.
But with that said, components should not typically listen to actions at all. They should only listen to updates to the store and reflect that update any way necessary. So with the points mentioned above, stores could listen to the "completed" or "failed" actions, and appropriately update their state to reflect the "completed" or "failed" actions. The components then render any "error" indications based on the store's state, not on actions directly.

Asynchronous data loading in flux stores

Say I have a TodoStore. The TodoStore is responsible for keeping my TODO items. Todo items are stored in a database.
I want to know what is the recommended way for loading all todo items into the store and how the views should interact with the store to load the TODO items on startup.
The first alternative is to create a loadTodos action that will retrieve the Todos from the database and emit a TODOS_LOADED event. Views will then call the loadTodos action and then listen to the TODOS_LOADED event and then update themselves by calling TodoStore.getTodos().
Another alternative is to not have a loadTodos action, and have a TodoStore.getTodos() that will return a promise with the existing TODO items. If the TodoStore has already loaded the TODO items, it just returns them; if not, then it will query from the database and return the retrieved items. In this case, even though the store now has loaded the TODO items, it will not emit a TODOS_LOADED event, since getTodos isn't an action.
function getTodos() {
if (loaded)
return Promise.resolve($todoItems);
else
return fetchTodoItemsFromDatabase().then(todoItems) {
loaded = true;
$todoItems = todoItems;
return $todoItems;
});
}
I'm sure many will say that that breaks the Flux architecture because the getTodos function is changing the store state, and store state should only be changed though actions sent in from the dispatcher.
However, if you consider that state for the TodoStore is the existing TODO items in the database, then getTodos isn't really changing any state. The TODO items are exactly the same, hence no view need to be updated or notified. The only thing is that now the store has already retrieved the data, so it is now cached in the store. From the View's perspective, it shouldn't really care about how the Store is implemented. It shouldn't really care if the store still needs to retrieve data from the database or not. All views care about is that they can use the Store to get the TODO items and that the Store will notify them when new TODO items are created, deleted, or changed.
Hence, in this scenario, views should just call TodoStore.getTodos() to render themselves on load, and register an event handler on TODO_CHANGE to be notified when they need to update themselves due to a addition, deletion, or change.
What do you think about these two solutions. Are they any other solutions?
The views do not have to be the entities that call loadTodos(). This can happen in a bootstrap file.
You're correct that you should try your best to restrict the data flow to actions inside the dispatch payload. Sometimes you need to derive data based on the state of other stores, and this is what Dispatcher.waitFor() is for.
What is Flux-like about your fetchTodoItemsFromDatabase() solution is that no other entity is setting data on the store. The store is updating itself. This is good.
My only serious criticism of this solution is that it could result in a delay in rendering if you are actually getting the initial data from the server. Ideally, you would send down some data with the HTML. You would also want to make sure to call for the stores' data within your controller-views' getInitialState() method.
Here is my opinion about that, very close to yours.
I maintain the state of my application in Store via Immutable.Record and Immutable.OrderedMap from Immutable.js
I have a top controller-view component that get its state from the Store.
Something such as the following :
function getInitialState() {
return {
todos: TodoStore.getAll()
}
}
TodoStore.getAll methods will retrieve the data from the server via a APIUtils.getTodos() request if it's internal _todos map is empty. I advocate for read data triggered in Store and write data triggered in ActionCreators.
By the time the request is processing, my component will render a simple loading spinner or something like that
When the request resolves, APIUtils trigger an action such as TODO_LIST_RECEIVE_SUCCESS or TODO_LIVE_RECEIVE_FAIL depending on the status of the response
My TodoStore will responds to these action by updating its internal state (populating it's internal Immutable.OrderedMap with Immutable.Record created from action payloads.
If you want to see an example through a basic implementation, take a look to this answer about React/Flux and xhr/routing/caching .
I know it's been a couple of years since this was asked, but it perfectly summed up the questions I am struggling with this week. So to help any others that may come across this question, I found this blog post that really helped me out by Nick Klepinger: "ngrx and Tour of Heroes".
It is specifically using Angular 2 and #ngrx/store, but answers your question very well.

Resources