Asynchronously maintaining state integrity in Redux - redux

Suppose I have a Redux store. Its state has the userCoordinates field
with the user's current coordinates:
{
userCoordinates: [3.1457, 2.1728],
...
}
Then suppose the store has 20 more fields with data that depends on the
coordinates:
{
...
nearbyFriends: ["Bob", "Jimmy"],
nearbyShops: ["Point & Click tools", "Bobby Ray's"],
address: "42 Enth Street, Townsville, Countryland",
weather: "Cloudy",
...
}
Values in all of these fields are loaded over HTTP from
various services, one separate request for each field.
Now suppose the user's geolocation is updated, and an action is dispatched that
changes the value of userCoordinates:
dispatch(updateCoords([1.6180, 1.4142]));
Now userCoordinates has new coordinates and all 20 other fields have invalid data.
If the goal is just to keep all the fields "eventually" valid, where do I make the requests to fetch the new data?
How the method changes if I use React-Redux and there are lots of components using many different combinations of these fields, with some fields used by more than one component and some fields not used at all?

Depending on how this is architected, I would do one of two things.
1: Compare states in your reducer. If there is a change in the user coordinates in the state, you could add something to the store like updateNeeded: true, then have a check wherever you are loading your other data so that if updateNeeded is true, you fire an ajax call to go get new data from the server.
2: If it is set up the right way, you could used componentWillReceiveProps to compare the props. If there is a change in userCoordinates, dispatch an action to go get the other data fields.
Of course, this would just be easier if you could just get all the data with one call...

Related

Redux / Flux Pattern for Fetching Data When Store Updates

I have what I believe is a very common scenario... I'm building a dashboard of components that will be driven by some datasource. At the top of the view would be a series of filters (e.g. a date range). When the date range is updated, the components on the screen would need to update their data based on the selected range. This would in turn force the individual components that are slave to that picker to need to fetch new data (async action/XHR) based on the newly selected range.
There can be many components on the screen and the user may wish to add/remove available displays, so it is not as simple as always refreshing the data for all components because they may or may not be present.
One way I thought to handle this was in the action dispatched when a new date range is selected was to figure out what components are on screen (derived from the Store) and dispatch async actions to fetch the data for those components. This seems like a lot of work will go into the DATE_CHANGED action.
Another alternative might be to detect date range changes in store.subscribe() callbacks from each of the components. This seems to decouple the logic to fetch the data from the action that caused this to happen. However, I thought it was bad practice (or even an error) to dispatch while dispatching. Sure I can wrap it in a setTimeout, but that feels wrong too.
Third thing that came to mind was just doing fetch calls directly in the component's store.subscribe() and dispatching when those return, but I thought this breaks the connect model.
This seems like a common pattern to fetch based on state changes, but I don't know where its best to put those. Any good documentation / examples on the above problem?
Don't use store.subscribe for this. When DATE_CHANGED reaches the reducer it's meant for, simply change the application state (I'm assuming the date range is part of the store somehow). So you have something like state.rangeStart and state.rangeEnd.
You didn't mention what view rendering library you're using, so I can only describe how this is typically done with React:
The components know wether they are currently mounted (visible) or not, so redux doesn't need to be concerned with that. What you need is a way to detect that state.rangeStart or state.rangeEnd changed.
In React there is a lifecycle hook for that (componentWillReceiveProps or getDerivedStateFromProps in the newest release). In this handler you dispatch async redux actions that fetch the data the component needs. Your view library will probably have something similar.
The components display some kind of "empty" or "loading" state while you're waiting for the new data typically. So a good practice is to invalidate/clear data from the store in the reducer that handles the DATE_CHANGED action. For example, if state.listOfThings (an array) entirely depends on the date range, you would set it to an empty array as soon as the date changes: return { ...state, listOfThings: [] }. This causes the components to display that data is being fetched again.
When all the async redux actions went through the REQUEST -> SUCCESS/FAILURE cycle and have populated the store with the data, connected components will automatically render it. This is kind of its own chapter, look into redux async actions if you need more information.
The tricky part are interdependencies between the components and the application they're rendering. If two different dashboard components for example want to fetch and render state.listOfThings for the current date range, you don't want to fetch this data twice. So there needs to be a way to detected that 1) the data range has changed but also 2) a request to fetch listOfThings is already on its way. This is usually done with boolean flags in the state: state.isFetchingListOfThings. The async actions fetching this data cause the reducer to set this flag to true. Your components need to be aware of this and dispatch actions conditionally: if (props.rangeStart !== nextProps.rangeStart && !nextProps.isFetchingListOfThings) { props.fetchListOfThings(); }.

Preventing reselect re-computing until all data actions are complete

I have a Redux (well, rxjs-store) structure that's very generic. Basically, my data-types are all different versions of a generic data structure called an "Element" (each defined by a different "ElementType").
The goal is I don't have to add a whole new set of data objects to the store, API, backend, database etc when a new data object comes along.
This is working well so far, but I've come across a problem with Redux & Reselect where the selector is re-calculating multiple times.
Here's an example. For my page I need to load 4 sets of Elements of a particular type:
// subscribe to data
const elements$ = Observable.combineLatest(
this.elementTypeManager.getElementTypeByName('DistributionNetworkNode'),
this.elementTypeManager.getElementTypeByName('Socket'),
this.elementTypeManager.getElementTypeByName('Area'),
this.elementTypeManager.getElementTypeByName('DemandType'))
.switchMap(nodeAndSocketTypes => Observable.combineLatest(
this.elementManager.getElementsByType(nodeAndSocketTypes[0].id),
this.elementManager.getElementsByType(nodeAndSocketTypes[1].id),
this.elementManager.getElementsByType(nodeAndSocketTypes[2].id),
this.elementManager.getElementsByType(nodeAndSocketTypes[3].id)));
The elementTypeManager and elementManager is the service that sends out a Redux Action, and uses reselect to query the store, like this:
public getElementsByType(elementTypeId: AAGUID): Observable<Elements> {
// dispatch an action to the store (triggers http and storing data)
this.store.dispatch(this.elementActions.loadElementsForType(elementTypeId));
// use reselect to query store
return this.store.select(getElementsByTypeFactory(elementTypeId));
}
The Load Elements action triggers a http request to retrieve the data, and emits a Store Elements Data action, which the reducer takes and puts in the store.
The reselect selector is pretty simple and looks like this:
export const getElements = (state: AppState) => state.elements;
export function getElementsByTypeFactory(elementTypeId: AAGUID, includeValues: boolean = false) {
return createSelector([getElements],
(elements: Elements) => elements.filter(e => e.elementTypeId === elementTypeId));
}
It takes the elements, and filters by the specified type.
The problem is, the actions are called in sequence:
Load Elements (DistributionNetworkNode)
Load Elements (Socket)
Load Elements (Area)
Load Elements (DemandType)
Then, stored in sequence:
Store Element Data (DistributionNetworkNode)
Store Element Data (Socket)
Store Element Data (Area)
Store Element Data (DemandType)
Each time the reducer puts data into the store, reselect recalculates. Of course, it hasn't finished putting all the data in yet, so the other selectors return 0 results.
As a result, my CombineLatest emits 4 times, one with just the DistributionNetworkNode data, one with the DistributionNetworkNode and Socket data, one with the DistributionNetworkNode, Socket and Area data etc etc.
This is a continuous problem with the way my data is structured and how I'm using re-select, and I'm not sure how to overcome it without compromising the generic way the data is structured.
How can make sure all my Store Element Data actions have taken place before querying/emitting data with reselect?

ngrx, How to have a starting state from an api?

I have my reducer with a starting state of an empty array:
folderReducer(state:Array<Folder> = [], action: Action)
I'd like to populate the starting state, so when I do
store.subscribe(s => ..)
The first item I get comes from the database. I assume the way of doing this is with ngrx/effects, but I'm not sure how.
Your store always has the initial state, that you define in the reducer-function. The initial states main purpose is to ensure that the application is able to start up and not run into any null-pointer-exceptions. And also it sets up your application to start making the first api-calls ect. - so you can think of it as a technical initial state.
If you want to fill your store with api-data on the startup, you would do that on the same way that you add/modify data during any other action - just that the action of "initially loading data" is not triggered by some user-interaction but through:
either when your root-component loads
or as part of a service in the constructor
In case you want to prevent specific components from showing anything until your API-call is done, you would have to adjust the display-components to display or hide data based on your state (e.g. by implementing a flag in your satet initialDataLoaded).
A dynamic initial state is now supported, see: https://github.com/ngrx/platform/blob/master/docs/store/api.md#initial-state-and-ahead-of-time-aot-compilation
Also see: https://github.com/ngrx/platform/issues/51
I would only do this if the database is local, otherwise the request will hold up loading of the application.

Redux: few lists with separate data managed by the same reducer

So, my application has two (or more) lists of articles that are both managed by the ArticlesReducer. And I have two associated actions:
LOAD_FAVOURITES
LOAD_FEED
And the state is:
State = {
feed: ArticleListState
favourites: ArticleListState
}
So, basically those two sub-states should have completely separate data (retrieved from the REST api) but handled it in the same manner. The only difference is what data is being managed.
ArticleListState has various associated actions like ADD_ITEM, REMOVE_ITEM, UPDATE_ITEM.
I decided to make a wrapper around the Action that also has a value responsible for identifying which sub-state should be updated.
Like so:
RoutedAction
{
action: Action,
route: string
}
And my parent reducer is now checking the route property of the RoutedAction and passes action to the appropriate sub-reducer which handles it as usual.
Can it be considered a good practice? If not, how would such use-case be normally implemented?

Initialize part of Redux store on demand

I'm migrating my Reflux based application to the Redux and came across one issue. I have many stores in the application (which is a good or bad thing about Reflux) and some of them get initialized only when a certain page trying to use the store. Basically, the store initialization happens then a component connected to the store is about to mount. When initialising the store I actually loading the data asynchronously.
To illustrate the current behavior:
Suppose I have 5 pages and 5 stores, where each page using one store. The data for initial store state loaded only when the user navigates to the respective page. So, on initial application load, I only load data for store1 and only when user navigating to the page2 I'll load data for store2.
Now, I replacing my Reflux stores with a single Redux store which is consist of 5 parts and I'm planning to implement reducers for each of the parts. Every page if going to be associated with a reducer (page1 -> reducer1, page2->reducer2 etc.). From my understanding of how Redux is supposed to work, each parts of the store will be initialized by a respective reducer and it all will happen at the store initialization time. In the real application, I have, 50 stores, and I assume it will results in 50 API calls immediately at the application initialization which is not good at all. Not all of that data is required for the initial page.
Therefore my question: Is there any way to load the initial store state on demand in Redux? Or how could it be approached?
I understand, that I can fire an action to load data into the store before navigating to the page, but in reality, pages connected to several store parts and it is not easy to figure out what actions I need to call (although it is possible).
A similar question has been asked here but my question more about how to not load everything on initialisation.
Yes you can.
When you create your reducer, it is an good idea to give the state argument an default value so that when it is never undefined.
When you first initialize your store, you have the opportunity to pass down an initialState object. So you can do something like this:
let initialState;
if (yourDemand) { // load the intialState on 'yourDemand'
initialState = {
page1: intial state for page1Reducer
page2: intial state for page2Reducer
page3: intial state for page3Reducer
}
}
const store = createStore(
reducers,
initialState
);

Resources