Asynchronous data loading in flux stores - flux

Say I have a TodoStore. The TodoStore is responsible for keeping my TODO items. Todo items are stored in a database.
I want to know what is the recommended way for loading all todo items into the store and how the views should interact with the store to load the TODO items on startup.
The first alternative is to create a loadTodos action that will retrieve the Todos from the database and emit a TODOS_LOADED event. Views will then call the loadTodos action and then listen to the TODOS_LOADED event and then update themselves by calling TodoStore.getTodos().
Another alternative is to not have a loadTodos action, and have a TodoStore.getTodos() that will return a promise with the existing TODO items. If the TodoStore has already loaded the TODO items, it just returns them; if not, then it will query from the database and return the retrieved items. In this case, even though the store now has loaded the TODO items, it will not emit a TODOS_LOADED event, since getTodos isn't an action.
function getTodos() {
if (loaded)
return Promise.resolve($todoItems);
else
return fetchTodoItemsFromDatabase().then(todoItems) {
loaded = true;
$todoItems = todoItems;
return $todoItems;
});
}
I'm sure many will say that that breaks the Flux architecture because the getTodos function is changing the store state, and store state should only be changed though actions sent in from the dispatcher.
However, if you consider that state for the TodoStore is the existing TODO items in the database, then getTodos isn't really changing any state. The TODO items are exactly the same, hence no view need to be updated or notified. The only thing is that now the store has already retrieved the data, so it is now cached in the store. From the View's perspective, it shouldn't really care about how the Store is implemented. It shouldn't really care if the store still needs to retrieve data from the database or not. All views care about is that they can use the Store to get the TODO items and that the Store will notify them when new TODO items are created, deleted, or changed.
Hence, in this scenario, views should just call TodoStore.getTodos() to render themselves on load, and register an event handler on TODO_CHANGE to be notified when they need to update themselves due to a addition, deletion, or change.
What do you think about these two solutions. Are they any other solutions?

The views do not have to be the entities that call loadTodos(). This can happen in a bootstrap file.
You're correct that you should try your best to restrict the data flow to actions inside the dispatch payload. Sometimes you need to derive data based on the state of other stores, and this is what Dispatcher.waitFor() is for.
What is Flux-like about your fetchTodoItemsFromDatabase() solution is that no other entity is setting data on the store. The store is updating itself. This is good.
My only serious criticism of this solution is that it could result in a delay in rendering if you are actually getting the initial data from the server. Ideally, you would send down some data with the HTML. You would also want to make sure to call for the stores' data within your controller-views' getInitialState() method.

Here is my opinion about that, very close to yours.
I maintain the state of my application in Store via Immutable.Record and Immutable.OrderedMap from Immutable.js
I have a top controller-view component that get its state from the Store.
Something such as the following :
function getInitialState() {
return {
todos: TodoStore.getAll()
}
}
TodoStore.getAll methods will retrieve the data from the server via a APIUtils.getTodos() request if it's internal _todos map is empty. I advocate for read data triggered in Store and write data triggered in ActionCreators.
By the time the request is processing, my component will render a simple loading spinner or something like that
When the request resolves, APIUtils trigger an action such as TODO_LIST_RECEIVE_SUCCESS or TODO_LIVE_RECEIVE_FAIL depending on the status of the response
My TodoStore will responds to these action by updating its internal state (populating it's internal Immutable.OrderedMap with Immutable.Record created from action payloads.
If you want to see an example through a basic implementation, take a look to this answer about React/Flux and xhr/routing/caching .

I know it's been a couple of years since this was asked, but it perfectly summed up the questions I am struggling with this week. So to help any others that may come across this question, I found this blog post that really helped me out by Nick Klepinger: "ngrx and Tour of Heroes".
It is specifically using Angular 2 and #ngrx/store, but answers your question very well.

Related

crudGetList action does not delete from state data not present in response

WARNING: This question is specific to react-admin framework
I'm trying to do an in app manual, that uses data from server to load content pages. To doing so I'm doing a custom page that fetches manual pages on componentDidMount. In this function I call react-admin crudGetList(resourceName, pagination, sortingById, filters), where filters is {and:[{condition},{language: currentLanguage}]} since I want to have the manual in different languages. I noticed that having pages in different languages in database and using crudGetList action with filters fetches the correct instances, however the state maintains old data. For example if I initially fetch data in English language, change language and go back to manual page, redux state will have pages for both languages instead of the current selected one.
Is this expected behaviour? Making the new request for manual pages shouldn't replace redux-state data to data coming from request? If is not expected should I open an issue?
React-admin uses a pattern called optimistic rendering. That means that if the app has fetched some entities in the past, if it needs to display these entities, it first shows the stale entities, then fetches the backend, and if the response differs, re-render the screen with up to date data.
For instance, when a user fetches a list of posts, react-admin stores these posts in a dictionary indexed by id:
{
123: { id: 123, title: "hello" },
456: { id: 456, title: "world" },
...
}
React-admin also stores the list of identifiers that the list should display:
[123, 456, ...]
Using these two properties, react-admin can now display the list. But it can also display the detail of a post without hitting the server first. So when a user clicks on an item in the list, react-admin uses the data from the first structure to display it right away, without waiting for the server response.
The purpose of optimistic rendering is performance: since the user doesn't need to wait for a round trip with the server, the interface is super snappy.
In your particular case, I understand that this can cause problems, because the store contains stale data that is not in the desired language. I suggest that you create a custom saga, which reacts to the language change action, and clears the store to avoid this kind of problem.
Check the documentation for custom sagas in the react-admin site:
https://marmelab.com/react-admin/Admin.html#customsagas
You have to configure how the redux store responds to new incoming data.
More specifically, this is what a "reducer" is for; your "action" (in your case crudGetList) feeds the data into the "reducer", which is just a function with instructions to the store on how it should adjust its shape based on the new data.
Somewhere in your app there's probably a reducer that responds to your fetch action, but it's configured to just shove the new results alongside the old, rather than replace them. It's very difficult to know, however, without seeing the code describing the entire redux "cycle".
The redux docs are excellent. I'd start there and make sure you have a good understanding if the entire flow of data through redux, and then go hunting for that reducer.
https://redux.js.org/basics/reducers

Meteor load static data once for several templates

I'm working with meteor and FlowRouter. I have a collection of a country's administrative divisions and the data is about 2000 documents. I read this data in several routes so at the moment I'm subscribing to the same collection every time I visit one of the routes that is using this data.
This is causing a slow performance and a waste of resources. Given that this collection doesn't change, is there any way to load or subscribe to this data once and have it available for the whole app or specific routes?
Maybe save the data in settings.json and have it available as an object would be better?
Thanks in advance for any help.
You need to keep the subscriptions active between routes. You can do this using this package (written by the same author as FlowRouter so it all works nicely together):
https://github.com/kadirahq/subs-manager
Alternatively, create a Meteor method to return the data and save it in your Session. In this case it won't be reactive, so it depends on your needs.
Any subscription you make that's external to the routing will be in global scope, which will then mean that data from that subscription is available everywhere. All you need to do is set up the subscription say in the root layout file for your site and then that data will be kept in your local minimongo store at all times.
The Todo list collection in the Todo app example here is an example of this, this is the code from that example:
Tasks = new Mongo.Collection("tasks");
if (Meteor.isServer) {
// This code only runs on the server
Meteor.publish("tasks", function () {
return Tasks.find();
});
}
if (Meteor.isClient) {
// This code only runs on the client
Meteor.subscribe("tasks");
You can then query that local data as you would normally.

Tracking ajax request status in a Flux application

We're refactoring a large Backbone application to use Flux to help solve some tight coupling and event / data flow issues. However, we haven't yet figured out how to handle cases where we need to know the status of a specific ajax request
When a controller component requests some data from a flux store, and that data has not yet been loaded, we trigger an ajax request to fetch the data. We dispatch one action when the request is initiated, and another on success or failure.
This is sufficient to load the correct data, and update the stores once the data has been loaded. But, we have some cases where we need to know whether a certain ajax request is pending or completed - sometimes just to display a spinner in one or more views, or sometimes to block other actions until the data is loaded.
Are there any patterns that people are using for this sort of behavior in flux/react apps? here are a few approaches I've considered:
Have a 'request status' store that knows whether there is a pending, completed, or failed request of any type. This works well for simple cases like 'is there a pending request for workout data', but becomes complicated if we want to get more granular 'is there a pending request for workout id 123'
Have all of the stores track whether the relevant data requests are pending or not, and return that status data as part of the store api - i.e. WorkoutStore.getWorkout would return something like { status: 'pending', data: {} }. The problem with this approach is that it seems like this sort of state shouldn't be mixed in with the domain data as it's really a separate concern. Also, now every consumer of the workout store api needs to handle this 'response with status' instead of just the relevant domain data
Ignore request status - either the data is there and the controller/view act on it, or the data isn't there and the controller/view don't act on it. Simpler, but probably not sufficient for our purposes
The solutions to this problem vary quite a bit based on the needs of the application, and I can't say that I know of a one-size-fits-all solution.
Often, #3 is fine, and your React components simply decide whether to show a spinner based on whether a prop is null.
When you need better tracking of requests, you may need this tracking at the level of the request itself, or you might instead need this at the level of the data that is being updated. These are two different needs that require similar, but slightly different approaches. Both solutions use a client-side id to track the request, like you have described in #1.
If the component that calls the action creator needs to know the state of the request, you create a requestID and hang on to that in this.state. Later, the component will examine a collection of requests passed down through props to see if the requestID is present as a key. If so, it can read the request status there, and clear the state. A RequestStore sounds like a fine place to store and manage that state.
However, if you need to know the status of the request at the level of a particular record, one way to manage this is to have your records in the store hold on to both a clientID and a more canonical (server-side) id. This way you can create the clientID as part of an optimistic update, and when the response comes back from the server, you can clear the clientID.
Another solution that we've been using on a few projects at Facebook is to create an action queue as an adjunct to the store. The action queue is a second storage area. All of your getters draw from both the store itself and the data in the action queue. So your optimistic updates don't actually update the store until the response comes back from the server.

VieModel collection not saved during State Save/Tombstone

If I lock my phone while running my application and unlock it say after 30 minutes or 60 minutes, my screen appears blank. All my data (its a huge list compare it to a user's twitter feed) which was in an Observable collection in my ViewModel has disappeared. When I refresh I get NullReferenceException. Note that I am not handling any state save while locking and unlocking the phone. Is that the reason for the loss of my data? How can I handle it? Since there is a limit on the state data which can be saved of 4Mb Max, will it affect the functioning of my application even if I do implement it?
[Update]
I have tried the following things:
1) http://www.scottlogic.co.uk/blog/colin/2011/05/a-simple-windows-phone-7-mvvm-tombstoning-example/
2) http://www.scottlogic.co.uk/blog/colin/2011/10/a-windows-phone-7-1-mango-mvvm-tombstoning-example/
and many more.
The problem which I now face is that my application's viewModel contains an observable collection which I have binded to the UI. This observable collection is a collection of my user-defined class which contains complex data members. One of them is a dictionary. When i try to save my viewModel using XMLSerialization it throws an error as XML serialization doesn't support Dictionary.
I have also tried to write my viewmodel after Data contract serialization onto the IS during App_Deactivated and retrieve it on App_Activated. But my collection is null on resume. On opening the IS file it shows that the collection was not written onto the file. Am I missing some key ingredient in-order to solve this problem?
Note: I need my list. I cannot refresh data.
I'd suggest that this is the wrong approach.
Tombstoning is designed to allow you to save your state, not your data. You want to store the following:
The page you're currently on
The parameters, if any, that were used to get your list of data that you are currently showing
Any selection state (has the user selected a row, etc)
Any page state (is it in edit-mode, etc)
Not all of these things will apply, but it should provide you with an idea of what you should be storing.
This will be a significantly smaller set of data using simple data types rather than large chains of complex objects.
So:
Store the properties/parameters that you use to get your data
When the app resumes go get your data again using the params. If this take a while give the user some form of progress notification. If you can't accurately do this then display activity on the screen until the load finishes so the user knows that something is happening.

What's the best way to implement deletion of user objects where there are multiple viewers of the object?

Let's say I have a GUI with multiple types of viewers of user objects. For example, a tree view, a list view and a diagram view. The three views show the same objects. If a user deletes an object from one view, I would like to fire off an event to notify the other two views. I currently do this by exposing an event on the object itself. So if the object is deleted from View 1, View 1 will call delete on the object, which will then fire an event to the subscribers (all 3 views). Each subscriber has the chance to cancel the deletion.
There are a few problems as I see it. If a subscriber cancels a deletion after another subscriber has already approved of the deletion, then I have to instruct those subscribers to undo the deletion.
Are there any good patterns to implement this kind of common scenario?
If an object is to be deleted from all views, or no view at all
Ask every subscriber if it's ok to delete the item; if yes:
Issue a "delete item" call to remove the object from the source, perform a soft delete or whatever you'd like
Update each view. This would be the observer part, listen for a "object deleted" call and take appropriate actions, for example manually remove the now deleted object from each view
If you always want the user to be able to delete the object from its own view:
Step 2. from above, with the addition that it's only been deleted either for 1) the user; or 2) that user in that view
Step 1. from above, and continue.. (might be skipped, depending on how much you'd like the views to be coherent)
The twist here is that each subscriber has the chance to cancel the deletion. Normally, when you use the words "view" and "subscribe", it means that you are being passive and just reacting to what you see.
That doesn't mean that what you're trying to do is impossible, but it's definitely tricky. For example, you could try to do a sort of two-phase commit, where you mark the object is deleted and then wait for all of the viewers to acknowledge the deletion before really removing the object. (This is basically the "ask every subscriber if it's OK to delete the item" approach that chelmertz suggests.) However, this means you need to know exactly how many viewers there are, and all viewers will need to respond before you can complete the deletion. Do you always have three viewers? Are there ever only two? What if there is an error in one of the viewers - Should the delete fail, or do you want to go ahead and delete the object anyway?
The nice thing about an event-driven system is that you don't normally have to worry about these sorts of questions: You just make your change to the model (in this case, delete the object) and fire a change event. You don't need to know anything about your viewers.
So, if this were my system, I would try to figure out a way to make model changes cancelable only before they are applied to the model, rather than trying to apply changes to other views through the model and then trying to roll back those changes later.

Resources