Deduplicating API calls with Redux Saga - redux

In part of my application I need to save an item state to the server.
However sometimes I will trigger a chain of submits each about 10ms away from the other.
State A
State B
State C
State D
In this case I should really only submit State D.
My current solution is to have a takeLatest() on a saga with
function* submitItemStateSaga(action: Action<SubmitItemStatePayload>) {
yield call(delay, THROTTLE_MS);
//saga body
}
This seems kinda of hacky to me. Do you think this is okay, or is there a better way to do it using the inbuilt throttle() function.

Is this 10ms determined by you or by your server's response? If the latter, sooner or later this approach will let you down.
That being said, you can do this
function* submitItemStateSaga(action) {
const { submit, cancel } = yield race({
submit: take('CONFIRM_SUBMISSION'),
cancel: take('CANCEL_SUBMISSION'),
})
if (submit) {
//saga body
}
}
function* actionWatcher() {
yield takeLatest('START_SUBMISSION', submitItemStateSaga)
}
And at the beginning of each submit on your chain you can dispatch the action to cancel the previous one and start the next. And then after you executed your entire chain you dispatch the action to confirm the submission.

It looks like like redux-saga's debounce() fits your use case better. It waits for incoming actions to slow down, then executes a task with the last action.
Both throttle() and debounce() are implemented via primitives like fork() and call(), and the implementations are provided in the docs. When the default behavior doesn't work for you, these implementations are a good start for writing your own custom solution.

Related

NgRx reducer function with condition

I have a side effect that detects the browser language and dispatches a browserLanguageSupported action if it is a language that my application can handle.
Now I have following reducer function that only updates the states preferredLanguage property in case it is not defined already. This is important because there are other actions that update this state property and I do not want a late browserLanguageSupported action to overwrite such a state update.
export interface State {
preferredLanguage: AppLanguage | undefined;
rehydrationComplete: boolean;
}
export const initialState: State = {
preferredLanguage: undefined,
rehydrationComplete: false
};
export const reducer = createReducer(
initialState,
on(LanguageActions.browserLanguageSupported, (state, {browserLanguage}) => {
if (!state.preferredLanguage) {
return {...state, preferredLanguage: browserLanguage};
}
return state;
})
);
Now for my question: Is it good practice to have such a condition in a reducer operator? The function itself is still pure. But I am not sure if it is good design or if I should solve it differently, lets say by adding state slice selection in the side effect that dispatches this action.
Btw. the reason I am not setting it directly in the initial state is because I get the browser language from an angular service and I am not sure if it is even possible to set initial feature state from service injection?
Best regards,
Pascal
I would to this the same way, so you get a đź‘Ť from me.
Adding a slice of the state into the effect just adds needless complexity.
The reducer contains the state, and it's OK to add logic to see if state needs to be updated or not.
Also, let's say you need to add this logic into another action/effect.
Having it in the reducer makes it easier to reuse if it's needed. Otherwise you end up with duplicate logic.
As long as the rejection (or mutation) of the data is irrelevant to the chain of actions & effects, this is absolutely valid.
However, it's worth noting that if the action in question triggers an effect which triggers an action, the triggered action will not know whether the data was rejected (or mutated) without checking the state—which is exactly what this pattern is attempting to avoid.
So, if you wanted to be able react to that rejection (or mutation), you would want to handle this in the effect. But, if you would proceed in exactly the same manner regardless of the result, then it belongs reducer.

Return a Promise from Redux Observable

Maybe I'm thinking about this wrong, but a common pattern I use with redux-thunk is to return a promise so I can perform some additional actions in the container object when something completes or fails.
Using Thunk as an example:
Action Creator:
const action = ({ data }) => (dispatch) =>
fetch(`some http url`)
.then(response => {
if(response.ok) {
return response.json();
}
return Promise.reject(response);
})
Somewhere in the connected component:
this.props.action("Some Data")
.then(console.log)
.catch(error => console.error("ERROR"));
Is there a clean way of doing something similar in Redux-Observable/Rxjs? Basically returning a promise from an action, calling an epic, that returns resolve or reject once the observable is complete.
Generally speaking, I try to shy people away from these patterns when using things like redux-observable/redux-saga. Instead, if you're waiting for an action you dispatched to update the store's state, rely on that state update itself or some sort of transactional metadata your reducers store in the state about it. e.g. { transactions: { "123": { isPending: true, error: null } }
However, if you really do want to do it, you can write (or use an existing) middleware that returns a Promise from dispatch that will resolve when some other specified action has been dispatched (presumably by your epics). redux-wait-for-action is one example (not a recommendation, though cause I haven't used any)
Keep in mind that Promises are not normally cancellable, so you could accidentally create a situation where a component starts waiting for an action, the user leaves that page and the component is unmounted, and you're still waiting for that other action--which can cause weirdness later, like if they come back to that page, etc.
If you're waiting to chain side effects, that belongs in your epics. Kick off a single action that an epic listens for, makes the side effect, then it emits another action to signal completion. Another epic listens for that completion action and then begins the other side effect, etc. You could put them all in a single monolithic epic, if you want, but I find the separation easier for testing and reuse.

meteor: how to stop asynchronous call?

Is it possible to stop (kill) asynchronous Call?
In my app I have at client side sth like:
Meteor.call('doCalculation', function(err, result) {
//do sth with result
});
'doCalculation' may take long time (this is ok) I dont want user to start new call when he/she has already one running call, I want to allow user to stop current call and submit new one. How correctly do this?
The only idea I have is to communicate between client and server using mongo. In some place in 'doCalculation' function I can observe some mongo document/collection and based on this do sth in the function (e.g. call exception). Do you have any better ideas?
You can use a semaphore for this purpose. When the semaphore is 1, requests are allowed to be sent. When the semaphore is 0, requests are not allowed to be sent. The semaphore should be 1 by default and just before you send the request, you need to set it to 0. When a response is successful, you set the semaphore back to 1.
As about the timeout: You could use a time out using setTimeout after sending the request, like this:
if (semaphore) {
var isTimedOut = false;
var isSuccess = false;
semaphore = 0; //No need to use var keyword, as this should be declared outside of this scope
Meteor.call('doCalculation', function(err, result) {
isSuccess = true;
//do sth with result
});
setTimeout(function() {
if (!isSuccess) {
isTimeout = true;
//do something else, to handle the time out state
}
}, 10000);
}
This is tricky, because you cannot generally set timeouts from the client's point of view. You don't need to, for a bunch of architectural reasons. The most important thing is that if you lose network connectivity or the server crashes (two cases timeouts are designed to manage), the client is aware immediately because it is disconnected. You can use Meteor.status().connected if this happens often.
It sounds like you're running a long calculation on the server. My suggestion is to return a calculationId immediately, and then update a collection with progress, e.g., CalculationProgresses.update(calculationId, {$set: {progress: currentProgress}}) as you calculate. Your UI can then update the progress reactively, in the most convenient way possible.
Note, that when you do run long calculations on the server, you need to occasionally "yield," giving the chance for other work to happen. Node, on which Meteor is based, is tricky for long calculations if you don't master this notion of yielding. In Meteor, you can yield easily by updating a collection (e.g., your progress collection). This will solve lots of problems you're probably experiencing as you write your application.
i think you need a server-side solution for this. if you go with a client-side solution, you don't handle 2 cases:
the user reloads their browser
the user uses 2 browsers
i would create these methods:
isCalculationActive() -- this checks if the user already has a calculation active. on the server, you can either keep that fact in memory or write it to the db. on the client, if this returns false, then you can proceed to call doCalculation(). if true, you can give the user a popup or alert or something to ask if they want to cancel and proceed.
doCalculation() -- this cancels any outstanding calculation by that user and starts a new one.
with these implemented, the user can reload their browser w/o affecting either the running calculation or correct behavior. and if they try a 2nd browser, everything should still work as expected.
if you want to give the user the option to simply stop the job and not start a new one, then you can simply create:
cancelCalculation() -- this cancels any outstanding calculation by that user.

How to have multiple reducers trigger updates based on a common set of actions without repeating yourself?

I would like many different redux actions in my app to all trigger common functionality in a specific reducer. I would like to avoid having to either repeat some flag in every action creator (like doThing: true) that the reducer looks for. I also don't want to have to have the reducer just look for every individual action that falls into this category, since that also requires someone to remember to do this every time they add a new action, like adding the flag.
I was thinking of dispatching a second action every time one of these actions is going to be dispatched. This would not be hard to do, but I'd rather not have 2 actions dispatched every time one thing happens. It seems like it would pollute the state history.
Is there a common way of solving this problem?
For more context to my specific problem, the specific feature is related to the API client my app uses to talk to our API. On every successful response, we'd like to do something in a reducer to update the state, and on every failed response, we'd like to do something else.
There are many different success and failure actions (such as ITEM_FETCH_SUCCESS or WIDGET_UPDATE_FAILURE), and adding a flag to all of them would be hard to remember to do when new ones are added.
Since all api requests go through a single function, that function COULD dispatch generic REQUEST_SUCCESS and REQUEST_FAILURE actions. But this would mean every response from the server would dispatch 2 actions (REQUEST_SUCCESS and ITEM_FETCH_SUCCESS). This is obviously not ideal since it would mean many more actions in my state history.
Assuming the generic REQUEST_SUCCESS and REQUEST_FAILURE actions are updating their own specific portions of the state-tree then it is fine to dispatch them as distinct actions. Doing this does not necessarily imply the pollution of your state history but can simply be a better description of the app's intentions.
ITEM_FETCH_SUCCESS: Change state for item
REQUEST_SUCCESS: Change state for request
WIDGET_UPDATE_FAILURE: Change state for widget
REQUEST_FAILURE: Change state for request
You can see that whilst the actions are intimately related, they are not necessarily the same thing as they change different parts of the state tree.
Accepting this, the question is: How best to implement the action-pairs so that adding new actions does not mean remembering to add its corresponding REQUEST_* partner?
I would consider applying a simple redux middleware component. This could intercept the return from your api and dispatch the appropriate REQUEST_* action automatically.
Here is an example from some live code. This middleware intercepts a disconnect event raised by a websocket and automatically dispatches a custom action as a result. It at least shows the principle:
//Dispatch a disconnect action when the websocket disconnects
//This is the custom action provided by the middleware
import io from 'socket.io-client'
import { actions } from './action'
const websocket = ({ websocketUrl }) => store => {
const socket = io(websocketUrl)
socket.on('disconnect', () => store.dispatch(actions.disconnect()))
}
export default websocket
//Apply the custom middleware via the redux createStore function
//Also include the thunk middleware because it is useful
import { applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import websocket from './middleware'
function websocketize (opts) {
return createStore => (reducers, initial, enhancer) => {
const middleware = applyMiddleware(thunk, websocket(opts))
return createStore(reducers, initial, middleware)
}
}
export default websocketize
// Create the top-level redux store passing in the custom middleware enhancer
const opts = {websocketUrl: env.WEBSOCKET_URL}
const store = createStore(reducers, websocketize(opts))
This implementation keeps everything inside your reducers as opposed to having logic outside in an interception(middleware). Both ways are valid.
Try a sub-reducer pattern. I usually feel gross when I see it used(because it is usually used wrong), but your situation sounds perfect.
Extract duplicate functionality out of your reducers to one single
sub-reducer.
Then pass that reducer as a function to all others that need it.
Then pass the action and state onto the sub-reducer.
The sub-reducer does it's thing and returns that slice of state to
your parent reducer to allow you to do whatever you want with it
there (ie return it, mutate some more, some logic).
Also if you are tired of worrying about typing out "all the stuff" for async then I highly recommend you try out redux-crud.js
It also is possible and a simple way to do that would be to give every action to one reducer and let it do that common mutation, in a single case:
case actionOne
actionTwo
actionThree
actionFour: {
//do common stuff here
}
. But you said it is not duplicated, it is similar, which means your case becomes complicated by branching logic. I also don't recommend this. Keep cases simple so you can easily catch invalid mutations. This should be a super power of redux that it is easy to catch mutation errors. And for this reason and many others I don't recommend normalizing data in the front end.

How to detect whether a child_added event is local?

In a web app I have this:
function onChildAdded(snapshot) {
// ...
}
someFirebaseLocation.on('child_added', onChildAdded);
I'm looking for a 100% reliable way to detect whether the child_added event is immediate, so that I can handle the two cases correctly: when after push() the function gets called immediately (sync) vs when the function gets called async.
Setting a flag before the push() call is not reliable I think. (Potential race condition when an async event comes in, and the flag might not get reset when there's an error).
Another option would be
var pushed = push(...);
and then in child_added
if (snap.name() === pushed)
but an incoming message could have the same .name() thus there could be collisions. The probability of a clash is debatable, but I'd prefer a simple and watertight way to get the info.
It would be great if I could do this:
function onChildAdded(snapshot, prevChildName, isImmediateEvent) {
if (isImmediateEvent) {
// Handle as sync event.
} else {
// Handle as async event.
}
}
someFirebaseLocation.on('child_added', onChildAdded);
or this
function onChildAdded(snapshot, prevChildName) {
if (snapshot.isFromImmediateEvent) {
// Handle as sync event.
} else {
// Handle as async event.
}
}
someFirebaseLocation.on('child_added', onChildAdded);
Is there some other reliable option? Otherwise I'll ask the Firebase guys whether they could generally pass a bool "isImmediateEvent" into the callback (after snapshot,prevChildName).
Tobi
You've covered the two options for now and either one should work reliably (see notes below). We might add features in the future to make this easier, but nothing concrete is planned at this point.
A couple notes:
Setting a flag should work fine. No async events will happen until after your synchronous code has finished running. You can avoid the error issue by using a try/finally block to reset it.
push() id's are designed to be universally unique, so you really shouldn't worry about conflicts.

Resources