Retry cancelled APIs on location change - javascript

Whenever the window location changes API calls called are canceled by the browser.
I am firing analytics events on-click of a button which also takes to a subdomain and refreshes the page, so there is a race condition now. Sometimes the event is resolved and sometimes it is canceled by the browser.
Tried to resolve it using service worker. Not getting callbacks in sync for canceled events
the possible workaround I found are as follows
wait for the event to be resolved before redirecting
store in local storage and fire on returning back
pass the event to be fired as query param to the subdomain and fire it there
Is there a better solution for this ?

Define a new Analytics component at global level that is not impacted with route changes.
Add a generic logic in Analytics component to receive data and hit
API.
Now location change of any component / destroy event will not impact cancelling of API call.
just the event need to be hit rest Analytics component will take care.
<Provider> -- redux store -->
**<Analytics >** - analytics logic , use redux to notify about event or other logic
<App></App> - has route and all other logic
</provider>

Related

Saving progress data with redux-saga when the user leaves the page

I'm building a system where a user is scored on the percentage of a video they have watched. If they leave/refresh / close the page I need to post their scoring data to an API endpoint.
I use the beforeunload event to fire the action when the user changes their location.
The action is handled using redux-saga.
I can see the action is being dispatched but it seems that the window closes before the call is made to the endpoint (chrome dev tools shows the call but with a status of canceled).
Is there any way to reliably ensure that the call is made to the endpoint? Surely there are a lot of sites (most notably e-learning ones) that handle this kind of behavior consistently, so there must be a solution.
Component:
componentDidMount() {
window.addEventListener('beforeunload', this.onUnload);
}
componentWillUnmount() {
window.removeEventListener('beforeunload', this.onUnload);
}
onUnload() {
this.props.postScore(params);
}
Any help is greatly appreciated.
If redux store is your app state, which is about to be go kaput, this is a rare time you have to bypass the store.
Just synchronously read the store and directly post to the api.
But even this is only saving the progress when the browser fires "unload".
If the page becomes unresponsive, or the browser simply crashes, the handler and api call will never execute.
A simple tactic would be to continually update progress every x seconds

How to to cancel pending asynchronous actions in React/Redux

Consider the following situation:
When user navigates to page, two asynchronous Redux actions are dispatched to fetch two sets of related data parallel. If either of these fetches fails, it will be detected by the component, which will render error component on next cycle, which in turn dispatches clearState action when mounting.
However, the other action is still to be resolved/rejected and will spoil the clean state. Objective is to interrupt this (and preferably many other) pending asynchronous actions, when clearState action creator is called. AFAIK, pending promise should either way resolve/response, no matter what.
First thing to come in my mind is to introduce INTERRUPTED flag to each reducer, but I can't wrap my head around how to use it to make following SUCCESS/ERROR action not to affect / return the default state.
How to do this, keeping the complexity at minimum?
I had a similar problem, I'm using jquery for making ajax request and redux-thunk middleware.
The solution is to make your action creators return the promise, this promise will be returned by the dispatch function, then you'll be able to abort it.
In my action creator I have something like this :
function doSomething() {
return (dispatch) => {
return $.ajax(...).done(...).fail(...);
}
}
Then in my component I have :
componentDidMount(){
this.myPromise = this.props.dispatch(doSomething());
}
somefnct() {
this.myPromise.abort();
}
Also have a look at this comment by Dan Abramov.
I was recently faced with the same problem, in which I had to cancel pending or stale async redux actions. I solved it by creating a cancel redux actions middleware.
In redux we have the concepts of middlewares. So when you are sending the request it will go through a list of middlewares. Once the api responds back its response will pass through a number of middlewares before it reaches redux store and eventually your UI.
Now suppose we have written a cancel middleware. The api request will go through this middleware when it being initiated and the api response will also go through this middleware when the api responds back.
Each api request in redux is associated with an action, each action has a type and payload.
Write a middleware, which whenever an api request is done stores the action type associated. Along with it, it stores a flag which states whether to discard this action. If an action of similar type is fired again, make this flag true which says discard this action. When the api response comes for the previous action since the discard flag for this api request has been set to true, send null as response from the middleware.
Look at this blog for detailed information about this approach. https://tech.treebo.com/redux-middlewares-an-approach-to-cancel-redux-actions-7e08b51b83ce

Can we turn off Firebase (JavaScript) local synchronization?

I am using Firebase JS SDK. Whenever I set a value to a node (with transaction), the "value" event on that node is fired (even before the transaction callback is invoked.
I find this feature is really annoying (though such could be convenient in some other cases). Can I turn this local sync feature off?
You can prevent the optimistic firing of local events for a transaction by passing in false for the applyLocally parameter. See https://www.firebase.com/docs/web/api/firebase/transaction.html
transaction(updateFunction, [onComplete], [applyLocally])
applyLocally Boolean *optional
By default, events are raised each time the transaction update function runs. So if it is run multiple times, you may see intermediate states. You can set this to false to suppress these intermediate states and instead wait until the transaction has completed before events are raised.

Dependent Actions in Flux and React JS

I'm currently having an issue in our Flux app where a component needs to first fetch the current user, and, if and only if the current user is fetched, fetch that user's notifications using a different Ajax call, as below:
_onCurrentUserChange: function() {
this.setState(getStateFromStores());
NotificationsActionCreator.getNotifications();
},
As you can see, I'm trying to dispatch the action to getNotifications after we know the currentUser has changed.
However, Flux doesn't allow for multiple dispatch of actions, and we've decided that all server requests should be dispatched through actions.
Thus, there's no way for me to wait on the currentUser to be received, and then dispatch the action to fetch the notifications.
What would be the correct Flux way to do this?
So I figured I'd post the way I fixed this for other people to see.
Basically, we ended up creating a function in our WebApiUtils module that makes sequential Ajax calls to the getCurrentUser and getNotificationsForCurrentUser backend routes. Then, we dispatch an action that contains both the currentUser and the notifications. The currentUser and notification stores both listen for this event, and each grabs the data it requires.
If you want to see the code for this, check out:
https://github.com/krazemon/repcoin/blob/master/js/api.js#L16
https://github.com/krazemon/repcoin/blob/master/js/stores/AuthStore.js#L59
https://github.com/krazemon/repcoin/blob/master/js/stores/NotificationsStore.js#L59-L62
I chose this solution because it doesn't violate the synchronous property of our stores and allows us to avoid cascading dispatch.

Callback, which fires on every user action

I would like to implement a timeout functionality in an AngularJS web app. Whenever a user does anything, a callback sets the timer to 10 minutes. When this timer expires on the client-side, a timeout request is sent to the server.
How can I implement a callback which fires from every user action? Maybe register onclick and onkeydown listener on the whole page/window?
According to the angular docs for $rootScope:
If you want to be notified whenever $digest() is called, you can
register a watchExpression function with $watch() with no listener.
I am not sure if $digest is called on every user action but it might be a good place to start.
FYI - How I implement a User time-out is as follows:
Authenticate user via server side API
Have API store the user in a cache with sliding expiration and pass back a session token
Have each secured API end-point require a session token and use this to fetch the User from the cache thus resetting the expiration timer.
If the User does not exist in the cache return a 403 forbidden error which your client code handles, presumably by sending the user back to login page. Actually I return a custom 403 that has a specific 'User Timeout' code and message so my client can handle the time-out gracefully.
This should work fine for most Single Page Apps because pretty much anything the user does causes a state change and most state changes involve a call to the server to fetch or save stuff. It misses out on minor user actions but in I have found that the real world use of this technique has sufficient resolution to be effective.
Looks like hackedbychinese.github.io/ng-idle does what I need

Categories

Resources