Troubleshooting: Redux & Redux Dev Tools -- Action "logjam" -- Actions are not appearing... then appearing all at once on next action - redux

Problem
Actions in my redux store are appearing to log-jam behind one another. I'm iterating through a set of thunks, which each call a number of actions to show they've started, succeeded, etc. When this happens, an action appears for a second in redux dev tools, then is erased.
If I post another action, then all the actions appear all at once, like container ships following the ever-given.
Link to gif of the issue
In this gif I connect to a testing database, afterwards, a number of operations dispatch. I can see those operations in the console, but not devTools. Then, I post another action via the onscreen button, and all the actions flow through at once.
I'm hunting for instances of mutated state, but all reducers destructure state into a new object via:
let newState = {...state}
Any tips?
EDIT:
When I dispatch the same operation from behind a button element, it works just fine. The code that's log jamming is being called by an event listener attached to an event emitter... maybe this has something to do with it?
After debugging, I've traced the problem back to the redux replaceReducer method. I call it 3 times in this sequence. The first and second invocation works fine, but on the third - the store stops receiving actions.
store.injectReducer = (key, asyncReducer) => {
storeTools.dispatchAction({type:"STORE_INJECT_REDUCER_" + key})
store.asyncReducers[key] = asyncReducer;
let combinedReducers = createReducer(store.asyncReducers);
storeTools.dispatchAction({type:"STORE_INJECT_REDUCER_" + key})
store.replaceReducer(combinedReducers);
storeTools.dispatchAction({type:"RESET"})
console.log("replaceReducer")
}
^^^
This code prints actions on the first 2 invocations, but on the third, it prints the first two actions, but not the third.

This bug was caused by invoking "replaceReducer" multiple times within the same thread. From what I now understand - if you call replaceReducer in a forEach loop... you're gunna have a bad time.
My solution was to create a function that stages multiple reducers - then calls replaceReducer once.
May folks from the future benefit from this knowledge.

Related

redux-injectors: Using yield select in saga before reducer and saga are injected

Hello this is my first question. I am trying to set up a project where modules along with the redux and sagas will be injected into the main app, using redux-injectors. In my sagas I want to use yield select, to check if an action has updated the state and then carry on. For example, when I post an image, I want to make sure there were no errors in posting the file and then move on. I use the following function:
export const imageErrors = (state: RootState): IImagesErrorState => state.image.errors
and then in the saga.ts file I use it as such:
if (imagesErrors?.postImageError !== null) {
throw imagesErrors.postImageError
}
this works fine as long as the state.image exists in the root state from the beginning. However, how do I do that when I want to inject this state later on using useInjectReducer and useInjectSaga? I obviously get an error
Property 'image' does not exist on type 'Reducer<CombinedState<{ user: CombinedState<{ auth: IAuthState; errors: IErrorState; }>; }>, AnyAction>'.ts(2339)
So how do we handle selectors of specific pieces of state, since state does not yet include them?
Thank you so much.
Can't talk about the Typescript part of things, but in terms of architecture you've got two options.
One is the obvious - that is to add conditions or ? everywhere to avoid errors from accessing missing properties, but that can get tedious quickly.
The other probably better option is to rethink your state & application chunks. What is this saga that is accessing state that isn't existing yet? Does it need to run before you have such state? If not, let's move the saga to the same chunk as the reducer. In the opposite case, where you need the saga to be running e.g. as part of the runtime chunk, then perhaps the image state should be in the runtime chunk as well.

How to make command to wait until all events triggered against it are completed successfully

I have came across a requirement where i want axon to wait untill all events in the eventbus fired against a particular Command finishes their execution. I will the brief the scenario:
I have a RestController which fires below command to create an application entity:
#RestController
class myController{
#PostMapping("/create")
#ResponseBody
public String create(
org.axonframework.commandhandling.gateway.CommandGateway.sendAndWait(new CreateApplicationCommand());
System.out.println(“in myController:: after sending CreateApplicationCommand”);
}
}
This command is being handled in the Aggregate, The Aggregate class is annotated with org.axonframework.spring.stereotype.Aggregate:
#Aggregate
class MyAggregate{
#CommandHandler //org.axonframework.commandhandling.CommandHandler
private MyAggregate(CreateApplicationCommand command) {
org.axonframework.modelling.command.AggregateLifecycle.apply(new AppCreatedEvent());
System.out.println(“in MyAggregate:: after firing AppCreatedEvent”);
}
#EventSourcingHandler //org.axonframework.eventsourcing.EventSourcingHandler
private void on(AppCreatedEvent appCreatedEvent) {
// Updates the state of the aggregate
this.id = appCreatedEvent.getId();
this.name = appCreatedEvent.getName();
System.out.println(“in MyAggregate:: after updating state”);
}
}
The AppCreatedEvent is handled at 2 places:
In the Aggregate itself, as we can see above.
In the projection class as below:
#EventHandler //org.axonframework.eventhandling.EventHandler
void on(AppCreatedEvent appCreatedEvent){
// persists into database
System.out.println(“in Projection:: after saving into database”);
}
The problem here is after catching the event at first place(i.e., inside aggregate) the call gets returned to myController.
i.e. The output here is:
in MyAggregate:: after firing AppCreatedEvent
in MyAggregate:: after updating state
in myController:: after sending CreateApplicationCommand
in Projection:: after saving into database
The output which i want is:
in MyAggregate:: after firing AppCreatedEvent
in MyAggregate:: after updating state
in Projection:: after saving into database
in myController:: after sending CreateApplicationCommand
In simple words, i want axon to wait untill all events triggered against a particular command are executed completely and then return to the class which triggered the command.
After searching on the forum i got to know that all sendAndWait does is wait until the handling of the command and publication of the events is finalized, and then i tired with Reactor Extension as well using below but got same results: org.axonframework.extensions.reactor.commandhandling.gateway.ReactorCommandGateway.send(new CreateApplicationCommand()).block();
Can someone please help me out.
Thanks in advance.
What would be best in your situation, #rohit, is to embrace the fact you are using an eventually consistent solution here. Thus, Command Handling is entirely separate from Event Handling, making the Query Models you create eventually consistent with the Command Model (your aggregates). Therefore, you wouldn't necessarily wait for the events exactly but react when the Query Model is present.
Embracing this comes down to building your application such that "yeah, I know my response might not be up to date now, but it might be somewhere in the near future." It is thus recommended to subscribe to the result you are interested in after or before the fact you have dispatched a command.
For example, you could see this as using WebSockets with the STOMP protocol, or you could tap into Project Reactor and use the Flux result type to receive the results as they go.
From your description, I assume you or your business have decided that the UI component should react in the (old-fashioned) synchronous way. There's nothing wrong with that, but it will bite your *ss when it comes to using something inherently eventually consistent like CQRS. You can, however, spoof the fact you are synchronous in your front-end, if you will.
To achieve this, I would recommend using Axon's Subscription Query to subscribe to the query model you know will be updated by the command you will send.
In pseudo-code, that would look a little bit like this:
public Result mySynchronousCall(String identifier) {
// Subscribe to the updates to come
SubscriptionQueryResult<Result> result = QueryGateway.subscriptionQuery(...);
// Issue command to update
CommandGateway.send(...);
// Wait on the Flux for the first result, and then close it
return result.updates()
.next()
.map(...)
.timeout(...)
.doFinally(it -> result.close());
}
You could see this being done in this sample WebFluxRest class, by the way.
Note that you are essentially closing the door to the front-end to tap into the asynchronous goodness by doing this. It'll work and allow you to wait for the result to be there as soon as it is there, but you'll lose some flexibility.

ngrx/store subscribe being called multiple times. Best place to unsubscribe?

I have an angular component that contains a NgModal popup.
I am subscribing to the ngrx store in the component.
The component's ngOnDestroy is never called because a new route is never called.
The user add's new Users via the modal popup.
The subscription(select) to the store for the 'user' state never unsubscribes and is being called many times.
What is the best way to handle the unsubscribing from a store.select() when ngOnDestroy() is never called?
The easiest way around this issue is don't subscribe in your component, do it in the template through the async pipe.
From the Angular Docs:
The async pipe subscribes to an Observable or Promise and returns the latest value it has emitted. When a new value is emitted, the async pipe marks the component to be checked for changes. When the component gets destroyed, the async pipe unsubscribes automatically to avoid potential memory leaks (emphasis added).
Since you mentioned ngOnDestroy is not called, then most likely the component persists and is hidden/shown as needed, in which case you'll just get the single subscribe.
You can also prevent multiple subscribe calls in the template by using the "async as" pattern, as explained by Todd Motto here.. Basically, when encountering the need for multiple async pipes in a template, use *ngIf="data$ | async as data" at a higher element and reference the resolved data below.
Destroying the Subscription inside ngOnDestroy() is useful, if you don't want that subscription to be called in different components
However, if the subscription is getting called multiple time within the same component you can solve this by using RxJs Operators
yourSelectorname$.pipe(skip(1)).pipe(take(1)).subscribe
or
yourSelectorname$.pipe(take(1)).subscribe
It depends according to your need

Do we have any lib to capture the state changes using sagas?

Basically need to build a warning modal , when user tries to move from current page/screen to another page , showing there are some saved changes .
Any implementations using redux and redux saga
Sagas are the lib for this - they watch for any action of a specified type. Navigation will take two actions: one to indicate that navigation is about to happen (which the saga will watch) and one to actually update the current page. The saga watches for actions of the first type and shows a warning dialog if the data has changed.
Ex:
function showWarning(action) {
if (/* data has been changed but not saved */) {
displayWarningDialog(action.pageToNavigateTo)
}
else {
// action that updates the page/location
completeNavigation(action.pageToNavigateTo)
}
}
function* mySaga() {
// NAVIGATE_TO_PAGE_X are the actions that get fired when a user changes pages
yield takeEvery("NAVIGATE_TO_PAGE_1", showWarning)
yield takeEvery("NAVIGATE_TO_PAGE_2", showWarning)
}
There is the amazing Redux DevTools for state debugging. This tool was built by Redux author himself.
Here are its features
Lets you inspect every state and action payload
Lets you go back in time by “cancelling” actions
If you change the reducer code, each “staged” action will be
re-evaluated
If the reducers throw, you will see during which action this
happened, and what the error was
With persistState() store enhancer, you can persist debug sessions
across page reloads
I've thought about this recently as well and been thinking about writing some form of middleware to intercept routing actions.
When intercepting a routing action, the middleware could determine if application state indicates the user is editing some unsaved data, and if so, dispatch a different action instead. That action should reduce state and cause a warning to render. The user could then confirm wanting to continue navigating by dispatching an action also intercepted by the middleware to continue the routing process.

Iron router + observechanges = repeated observechanges handler calls?

I'm attempting to use observechanges with iron router but they don't seem to be compatible at all.
Router.route('/gaming', {
waitOn: function() {
return Meteor.subscribe('chat', function() {
window.chatmessagesCache = new ReactiveVar;
chatmessagesCache.set([]);
return chat.find().observeChanges({
added: function(id, doc) {
var tmpArr;
tmpArr = chatmessagesCache.get();
tmpArr.push(doc);
return chatmessagesCache.set(tmpArr);
}
});
});
}
If I leave the route and come back to it, observechanges begins being handled as many times as I've navigated away and returned, for each new record. What's the deal?
If I use subs manager it works as expected, but I don't understand why Meteor.subscribe inside waitOn is so cache/subscription unaware when it ALREADY gets called multiple times per load. Why!? I can't decipher what's causing this behavior at all.
Also, what I'm trying to accomplish is simple. I want to let chat messages that the user's client has received remain on the page even if the chat cursor is no longer publishing them (I'm publishing the last 10 chat messages)
Iron router has reactivity built in, which means when something inside your route function is invalidated, it will repeat the function as well as anything reactive with a Router.current(). These unexpected invalidation runs are a primary reason why folks made the exodus to flow router.
To solve this, you'll want to abstract your code away from the router. You can leave the sub, but I'd suggest you remove the sub's callback from the waitOn and move it into an onRendered callback. If you don't want the history loaded in chunks, you can first do a var collectionCount = chat.find({},{reactive:false}).count() on how many docs are in the collection & then in the added callback you can do something like if (++currentCount === collectionCount) /* add stuff */ to add al the records to the history when it reaches the last record.
On a bigger picture level, consider eliminating the observeChanges & just do an #each over the chat collection in spacebars to show your messages. Fewer cycles, cleaner code.
Iron router just has no management of observations you created yet it manages subscriptions itself, hence the multiple additions.
I figured this out by using a window level variable to check if I'm observing. Even in cases when the subscription is unhooked by iron, if I go back and never re-add the handler, the original observation hook still runs (!). ALSO, if you navigate away and drop the subscription, the handler is no longer called--which is the behavior I want in this scenario (This is all very insane behavior but at least it's now predictable to me )
This is caused by the fact that subscriptions != collections and the API for observations doesn't seem to expose any metadata, unfortunately, so I don't know how the iron router maintainers would account for this. Not to mention you return iron router a subscription, not a collection.
#Matt K if you were correct, this would always be an infinite loop (which admittedly I had a bunch of while trying to solve this) but the posted code is adding too many handlers, not looping indefinitely. Thanks for the post though.
This is what I settled on
Router.route('/gaming',
waitOn: ->
Meteor.subscribe('chat', ->
window.chatmessagesCache = new ReactiveVar(chat.find().fetch().reverse())
if !window.chatListening
window.chatListening = true
after = chat.find().count()
chat.find().observe(
added: _.after(after + 1,(doc) ->
tmpArr = chatmessagesCache.get()
tmpArr.push(doc)
chatmessagesCache.set(tmpArr))
changed : (id, doc) ->
))
I really just wanted to test out a pattern of locally "disconnected" documents. I still may use subs manager because it keeps subscriptions and their handlers alive without rerunning them constantly (which was rerunning the sub handler, which was adding multiple observations)

Resources