Sending requests without losing data & keep chronological order - redux

I have a reactJS webapp that uses redux saga & axios to send
web requests for selecting products.
It uses takeEvery for the saga at the moment.
The problems:
Selecting products: If users select products very fast (by clicking checkboxes) there is request data missing. For instance: user clicks products: a,b,c,d,e,f but b & c are for instance not selected.
Toggling: I have tried to use the takeLatest but in case users select & deselect products very fast, there is a race condition / the order is not preserved.
As an example: user really quickly selects: product a, cancels it, selects it, cancels it, selects it
and the final status is 'cancel' although the last action was selecting it.
How it should work:
Selecting products: If a user selects products very fast, all of them should correctly be added.
Example: user adds a,b,c,d,e,f,g,h,i so all of these should be added.
Toggling: If a user toggles something repetitive & quickly, the final status has to match the latest action
As an example: User selects, cancels, selects, cancels => final state is cancel
Moreover: User selects => cancels => selects => final state id select
What is the appropriate technical way to implement this ?

You might be unwittingly opening a can of worms here, depending on how much of a perfectionist you are and how reliable you need your app to be. And if you want optimistic updates, oh boy.
Things can get way more difficult, because HTTP requests are not necessarily guaranteed to arrive in order nor are the responses from the server.
Same goes for your database, which can run into concurrency issues and so you might want to start versioning your data.
There are some strategies you can use to reduce such problems.
Write your endpoints in non stateful way or minimize the amount of sent state. For example, instead of sending list of all selected items, just send the single item that should be toggled.
Take advantage of the throttle & debounce saga effects, they will help you with request delays, skips and distribution to minimize the chances of stuff going out of order.
Consider blocking user action until you get a response back where it makes sense
Be aware that canceling a saga (e.g. through the takeLatest effect) doesn't cancel the ajax request itself (it will still arrive on your BE no matter what). It will only cancel the saga itself (in other words it will prevent your app from running the code after you would receive the data from BE)
Also be aware that using takeLatest to cancel sagas is limited. E.g. if you have only single entity you might always want to cancel the previous saga, because data from the previous response are no longer relevant.
yield takeLatest(DO_ACTION, actionSaga);
But what if you have the same action on top of multiple entities and you want to cancel the previous action only if you work with the same entity but not when it is another entity? If the list of entities is static, you can use the function pattern instead:
yield takeLatest(action => action.type === DO_ACTION && action.id = 'foo', actionSaga);
yield takeLatest(action => action.type === DO_ACTION && action.id = 'bar', actionSaga);
yield takeLatest(action => action.type === DO_ACTION && action.id = 'baz', actionSaga);
Once the list gets too long or if you have dynamic ids/amount of entities you will need to write your own logic using infinite cycles, id:task map & cancel effect.
export function* dynamicIdWatcher() {
const taskMap = {}
while (true) {
let action = yield take(DO_ACTION)
if (taskMap[action.id]) yield cancel(taskMap[action.id])
const workerTask = yield fork(actionSaga, action)
taskMap[action.id] = workerTask
}
}
Sorry for not really answering your question, but I hope some of what I wrote can still be useful in figuring out your use case :)

Related

Redux how to update computed state

I'm learning redux (in Flutter using Firestore, but I don't think that matters), trying to get beyond the basics, and I'm confused about where 'computed state' (not even sure what to call it) should go.
Say I have app state like this:
user: an instance of user
movies: a list of user's movies
recentFavoriteMovie: one of the movies above that user has marked "favorite" and has the most recent creation date
I'm able to set user (login success action) and request user's movies (login success middleware). When the movies query completes, I'm confused about where to initialize recentFavoriteMovie. There seems to be many choices....
SetMovies middleware can compute it, and then call a SetRecentFavorite action.
Can the SetMovies reducer do it? Or is that considered a side-effect of the reducer, which is not allowed?
I'd like to do it lazily. Is it considered okay in redux to give the app state object a method that computes and caches it? If so, I'd still need to clear the cached value when a new movie list is set. But that seems like the same problem as (b) above.
I could put the movies property and favoriteMovies (property or method) in my user object (they kinda belong there), and then just dispatch an UpdateUser action each time one or the other changes. In general, I don't know when/whether to "promote" some sub attribute of my app state to the top level so the app can react to it.
Are all or any of these valid choices? I hope this makes sense as a question. I might even be too behind the curve to ask this properly.
You almost ther with computed state. From documentation
Reselect provides a function createSelector for creating memoized selectors. createSelector takes an array of input-selectors and a transform function as its arguments. If the Redux state tree is changed in a way that causes the value of an input-selector to change, the selector will call its transform function with the values of the input-selectors as arguments and return the result. If the values of the input-selectors are the same as the previous call to the selector, it will return the previously computed value instead of calling the transform function.
That is essentially what yu want with lazy selection of movies.
In state you store user and movies. Some movies marked as favorites for specific user (so when user marks movie as favorite you only modify movies and not re-run selector).
When some component needs list of favorite movies, it calls selector which computes derived state (list of favorite movies) and returns it. Also selector will memorize results and recompute them only when store changes but not on each render.
This approach can be considered best practice as you may later implement some filters for movies list and selectors will help to extract filtered list of movies.
When using selector you don't required to store selected data (list of favorite movies) in you store.
Computed state is used in mapStateToPros for each component that require computed state like so
const makeMapStateToProps = () => {
const getFavoriteMovies = makeGetFavoriteMovies();
const mapStateToProps = (state) => (
{
favoriteMovies: getFavoriteMovies(state.user, state.movies),
movies: state.movies,
user: state.user
});
return mapStateToProps;
}
And makeGetFavoriteMovies may look like
const getFavoriteMovies = (state) => state.movies;
const getUser = (state) => state.user;
export function makeGetFavoriteMovies () {
return createSelector(
[getFavoriteMovies, getUser],
(favoriteMovies, user) => {
// Here can be complicated logic to select only favorite movies
return movies.filter (movie => movie.isFavorite);
}
);
)
Reducer and/or middleware can compute favorite movies list. But this is not their responsibility. So to better separate concerns its better to use selectors for this task.
Also there is not reason for specific middleware (ogin success middleware). You may implement logic in actions and reducer.

How to do a simple notification system with redux-observable?

I'm trying to do a simple notification system with redux-observable. I'm new to rxjs so I'm having a hard time doing it.
What I'm trying to do is:
Dispatch an intent to display a notification
Detect the intent with an Epic
Dispatch the action that inserts the new notification
Wait 3 seconds
Dispatch another action that deletes the old notification
This is my Epic:
import { NOTIFICATION_DISPLAY_REQUESTED } from '../actions/actionTypes';
import { displayNotification, hideNotification } from '../actions/notifications';
export const requestNotificationEpic = (action$, store) =>
action$.ofType(NOTIFICATION_DISPLAY_REQUESTED)
.mapTo(displayNotification(action$.notification))
.delay(3000)
.mapTo(hideNotification(action$.notification));
What really happens is that NOTIFICATION_DISPLAY_REQUESTED is dispatched, and 3 seconds later, hideNotification is dispatched. displayNotification never happens.
I could just dispatch displayNotification from the view, delay 3 seconds and then dispatch hideNotification. But later I want to delete the last notification before adding a new one if there are more than 3 active notifications. That's why I dispatch displayNotification manually from inside the epic in this simple case.
So, how do I achieve this? Sorry if this is super simple question, I'm just new to all this and need some help.
Note: I know redux-saga exists, is just that redux-obsevable made more sense to me.
If you're new to RxJS, this isn't so simple :)
Couple things up front:
Operator chains
An Epic is a function which takes a stream of actions and returns a stream of actions. Actions in, actions out. The functions you chain to transform matching actions are called operators. Chaining operators is a lot like chaining garden hoses or power cords--the values flow from one to the other. It's also very similar to just chaining regular functions like third(second(first())) except that Observables have an additional dimension of time, so the operators are applied on each value that flows through them.
So if you say stream.mapTo(x).mapTo(y) the fact that you firsted mapped to x is made meaningless when you .mapTo(y) since mapTo ignores the source's values and instead just maps it to the one provided.
If instead you used map, it might become more apparant:
stream.map(value => 'a message').map(message => message + '!!!')
Just to be claer, this chaining of operators stuff is RxJS, not specific to redux-observable, which is more a pattern of using idiomatic RxJS with a tiny amount of glue into redux.
action$ is an Observable (technically ActionsObservable)
The argument action$ is an Observable of actions, not an actual action itself. So action$.notification will be undefined. That's one of the reasons people commonly use the dollar sign suffix, to denote it is a stream of those things.
Consider only have 2 actions, not 3
Your example shows you using three actions NOTIFICATION_DISPLAY_REQUESTED and two others to show and hide the notifications. In this case, the original intent action is basically the same as displayNotification() because it would be dispatched synchronously after the other.
Consider only have two actions, one for "show this notification" and another for "hide this notification". While this isn't a rule, it can often simplify your code and increase performance since your reducers don't have to run twice.
This is what it would look like in your case (name things however you'd like, of course):
export const displayNotificationEpic = (action$, store) =>
action$.ofType(DISPLAY_NOTIFICATION)
.delay(3000)
.map(action => hideNotification(action.notification));
// UI code kicks it off some how...
store.dispatch(displayNotification('hello world'));
Your reducers would then receive DISPLAY_NOTIFICATION and then 3 seconds later HIDE_NOTIFICATION (order whatever).
Also, cruicial to remember rom the redux-observable docs:
REMEMBER: Epics run alongside the normal Redux dispatch channel, after the reducers have already received them. When you map an action to another one, you are not preventing the original action from reaching the reducers; that action has already been through them!
Solution
Although I suggest using only two actions in this case (see above), I do want to directly answer your question! Since RxJS is a very flexible library there are many ways of accomplishing what you're asking for.
Here a couple:
One epic, using concat
The concat operator is used subscribe to all the provided Observables one at a time, moving onto the next one only when the current one completes. It "drains" each Observable one at a time.
If we wanted to create a stream that emits one action, waits 3000 ms then emits a different one, you could do this:
Observable.of(displayNotification(action.notification))
.concat(
Observable.of(hideNotification(action.notification))
.delay(3000)
)
Or this:
Observable.concat(
Observable.of(displayNotification(action.notification)),
Observable.of(hideNotification(action.notification))
.delay(3000)
)
In this case, they have the exact same effect. The key is that we are applying the delay to different Observable than the first--because we only want to delay the second action. We isolate them.
To use inside your epic, you'll need a merging strategy operator like mergeMap, switchMap, etc. These are very important to learn well as they're used very often in RxJS.
export const requestNotificationEpic = (action$, store) =>
action$.ofType(NOTIFICATION_DISPLAY_REQUESTED)
.mergeMap(action =>
Observable.concat(
Observable.of(displayNotification(action.notification)),
Observable.of(hideNotification(action.notification))
.delay(3000)
)
);
Two different epics
Another way of doing this would be to create two different epics. One is responsible for maping the first second to the second, the other for waiting 3 seconds before hiding.
export const requestNotificationEpic = (action$, store) =>
action$.ofType(NOTIFICATION_DISPLAY_REQUESTED)
.map(action => displayNotification(action.notification));
export const displayNotificationEpic = (action$, store) =>
action$.ofType(DISPLAY_NOTIFICATION)
.delay(3000)
.map(action => hideNotification(action.notification));
This works because epics can match against all actions, even ones that other epics have emitted! This allows clean separation, composition, and testing.
This example (to me) better demonstrates that having two intent actions is unneccesary for this example, but there may be requirements you didn't provide that justify it.
If this was very confusing, I would recommend diving deep into RxJS first. Tutorials, videos, workshops, etc. This is only skimming the surface, it gets much much deeper, but the payout is great for most people who stick with it.

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 handle cross-cutting concerns in redux reducers and actions

Given a use case like the one in this question:
Best way to update related state fields with split reducers?
What is the best practice for dealing with actions in reducers that depend on state outside of their own state? The author of the question above ended up just passing the entire state tree as a third argument to every reducer. This seems heavy-handed and risky. The Redux FAQ lists the following potential solutions:
If a reducer needs to know data from another slice of state, the state tree shape may need to be reorganized so that a single reducer is handling more of the data.
You may need to write some custom functions for handling some of these actions. This may require replacing combineReducers with your own top-level reducer function.
You can also use a utility such as reduce-reducers to run combineReducers to handle most actions, but also run a more specialized reducer for specific actions that cross state slices.
Async action creators such as redux-thunk have access to the entire state through getState(). An action creator can retrieve additional data from the state and put it in an action, so that each reducer has enough information to update its own state slice.
In my use case, I have an action "continue" that determines what page a user is allowed to go to in a multiple-form / multi-step process, and since this depends on pretty much the entire app state, I can't handle it in any of my child reducers. For now, I've pulled the store into the action creator. I use the current state of the store to calculate an action object that fires to my "page" reducer, which changes the active page. I will probably install redux-thunk and use getState() in this action creator, but I'm not committed to this approach yet.
I guess this isn't too bad of a solution since there is only one action (so far) that must be handled this way. I'm just wondering if there is a better solution, or if there is a way to re-structure my state and reducers to make it easier, or if what I'm doing is within best practices for Redux. If there are any similar examples out there, that would be helpful also.
To give some more context, my state tree currently looks like this:
{
order: order.result,
items: order.entities.items,
activePage: {
id: 'fulfillment'
// page info
},
pagesById: { // all the possible pages
fulfillment: {
id: 'fulfillment'
// page info
}
}
}
The active page is the page / section in which the user must enter data in order to proceed to the next page). Determining the active page almost always depends on the items state and sometimes depends on order state. The end result is an app where the user fills out a few forms in succession, hitting continue once the form is valid. On continue the app determines the next page needed and displays it, and so on.
EDIT: We've tried the approach of implementing a "global" reducer in combination with child reducers.
The implementation is like this...
const global = (currentState = initialState, action) => {
switch (action.type) {
default:
return currentState
}
}
const subReducers = combineReducers({
order,
meta
})
export default function (currentState = initialState, action) {
var nextState = global(currentState, action)
return subReducers(nextState, action)
}
The global reducer is first run on the whole app state, then the result of that is fed to the child reducers. I like the fact that I'm no longer putting a bunch of logic in action creators just to read different parts of state.
I believe this is in alignment with the principles of redux since every action still hits every reducer, and the order in which reducers are called is always the same. Any thoughts on this implementation?
EDIT: We are now using router libraries to handle the page state, so activePage and pagesById are gone.
If state.activePage depends of state.order and state.items, you may subscribe to the store and in case of modifications on "order" or "items" then dispatch a "checkPage" action which can set another active page if necessary. One way should to connect on a "top component" order and items, listen their values and change active page/redirect
Not easy to understand your concern, I hope my message will help. Good luck

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