Comments (17)
Just a few thoughts on this ... the store could hold a record of any item ids waiting for a response and could expose this through a function to views. ie getItemsBeingFetched
.
Then if this array has any items in it the view could show a loading state, or if it is only concerned with a particular id it would check for that?
from flux.
@davetayls yeah, we are having a discussion on #reactjs and the store would create an representation of item 123, such as: {id: 123, status: loading} and clients could then query the store for loading items and we could easily cancel repetitive loading calls for the same item if it's already loading.
from flux.
@abergs that sounds like a good idea, my only thought would be when another view requests allItems
what would you return? I guess all currently known items to exist. Would also be good to think about how this would cater for delete
or put
changes?? 👍
from flux.
@abergs Stores should not directly create actions. This will lead to a dispatch-within-a-dispatch problem and an unstable state. You can just make the async call to the web API directly from the store (or by directly calling a method in a Utils module), and then create an action within the error/success callback.
I like the idea of {id: 123, status: loading}
, as it will allow the user to continue to work with the rest of the app.
from flux.
@fisherwebdev Any chance you could integrate this into one of the examples? You might recall I asked something very similar on StackOverflow regarding this type of flow.
Ideally it would cover the original points from @abergs, but also writing asychronous data using actions and why cascading events are a bad idea.
from flux.
The excellent interview of Ian Obermiller also has a lot of helpful tips on this subject.
from flux.
@abergs Also check out https://github.com/ianobermiller/nuclearmail
from flux.
^^^ nice! I hadn't seen that project of Ian's before. Thanks for that link.
from flux.
I've tried implementing the approach outlined in the interview with Ian and the Nuclearmail project. At first avoiding actions for reads make a lot of sense as it simplifies many aspects of an app.
However as soon as multiple components attempt to read the same data at the same time the approach becomes quite complex.
Consider two lists of todos where both show the exact same list of todos. List A would attempt to fetch todos from the store, the store calls an API function and stores the list of todos. List B however is attempting to do the exact same thing at the same time, this forces you to not only cache the underlying data but also currently active requests/promises so that you don't perform duplicate requests.
This becomes increasingly complex when something like pagination is added to the mix. Let's say you again have two paginated lists of todos, but this time list A shows todos 10-20 and B 1-20, obviously there's no point in fetching 10-20 as they will be included in the 1-20 request.
from flux.
@fisherwebdev by saying :
You can just make the async call to the web API directly from the store (or by directly calling a method in a Utils module)
do you imply that calling the webapi utils from a store dispatchToken is fine ? I just did it and it fixed my issue with Dispatch called during dispatch. However it feels kind of weird doing that in the perspective of that diagram :
from flux.
The most important Flux data flow diagram is this:
Action -> Dispatcher -> Store -> View
The rest, while suggesting best practices, are really just implementation details. How data leaves the system and goes to the server does not matter very much. What matters more is how it enters the system and flows through it. Always start the flow with dispatched actions.
from flux.
I played around a bit with this approach today:
Edit: I realize this has some Reflux specific code but it should be quite similar in "pure" Flux
Store:
var ProductStore = Reflux.createStore({
init() {
this.products = {};
},
get(id) {
// Attempt to fetch product from cache
var product = this.products[id];
// If no product was found, create a placeholder while we load it
if (!product) {
product = this.products[id] = {
id: id,
loading: true
};
// Fetch the product from our API
API.get('/products/' + id)
.then(res => {
this.products[id] = res.data;
this.trigger();
});
}
// Return either the actual product or the placeholder
return product;
}
});
Component:
var ProductDetailPage = React.createClass({
getInitialState() {
return this.getStateFromStores();
},
getStateFromStores() {
var productId = this.getQuery().productId;
return {
product: ProductStore.get(productId)
};
},
onStoreChange() {
this.setState(this.getStateFromStores());
},
componentDidMount() {
this.listenTo(ProductStore, this.onStoreChange);
},
render() {
var product = this.state.product;
return product.loading ? <Loader /> : <ProductDetails product={product} />;
}
});
I've yet to play around with list type responses yet, but I'm leaning towards wrapping list results in a type of "query"-object that includes the list of data along with hasMore
, total
etc. for the given query.
from flux.
This is really interesting. In my angular apps I tend to work heavily with promises. I essentially have stores that maintain a promise based cache of things, and each method makes the relevant API request if necessary to populate the cache. for example:
var itemCache = {}
function makeApiRequest(url){
return new Promise(function(res, err){
//fetch data from server..
res(data);
});
}
function copy(item){
//deep copy item...
return angular.copy( item );
}
function getItem(id){
//promise for item in cache? return a copy of it
if(itemCache[id]) {
return itemCache[id].then(copy)
}
//else, immediately cache promise for item:
var itemPromise = itemCache[id] = makeApiRequest("url/for/item/"+id);
return itemPromise;
}
//common usage (we can act on "loading" state easily):
isLoadingData = true;
getItem(12).then(function(item){
isLoadingData = false
/* do something with item */
})
The things I like about this approach are
- Something using this store doesnt care where the data is coming from. It might take a few seconds or it might be instant, but it's handled the same in either case. No need to keep track of loading state. An interested view can do that very easily if it cares, as above.
- It's really easy in most cases to ensure that a request for something never happens more than once. The moment there is a promise in the cache, every subsequent request will just be handed that same thing.
However, the downsides are that you end up working with promises throughout the system, which means everything becomes asyns. Also, I imagine storing promises is a less economical approach in terms of memory.
In practise I haven't found these to be a problem (yet), but I am intrigued to see whether a flux style architecture, which eliminates promises and async code from everything but the web API code, can do it better, so I'll be having a go at it for my next project.
@simenbrekken I like your approach. It makes total sense to me to make the XHR request from inside the store, as the store is the thing that knows what exactly it needs to get, and in my mind is responsible for, well, storing whatever data is asked for. Knowing exactly what data it is storing might allow it to be cleverer with regard to making XHR requests as well (eg batching multiple requests for different item details).
from flux.
Thank you for reporting this issue and appreciate your patience. We've notified the core team for an update on this issue. We're looking for a response within the next 30 days or the issue may be closed.
from flux.
Is there any update on this issue?
from flux.
@igo88 This is one approach that we use quite heavily at Facebook: https://github.com/staltz/flux-challenge/tree/master/submissions/kyldvs
Relevant files are SithStore and SithDataManager.
from flux.
Going to close this out since I feel like the example above demonstrates solutions to most aspects of async data in flux. If you would still like to discuss this issue further please feel free to re-open!
from flux.
Related Issues (20)
- Cannot dispatch in the middle of a dispatch. HOT 1
- New Flux store instance created upon second component referencing store HOT 7
- Flux Utils + Hooks HOT 6
- TypeError: Class constructor App cannot be invoked without 'new' HOT 1
- Using version of fbjs that depends on unsupported core-js module HOT 2
- Please update the following components: FluxContainer(containerClass) React 16.10.2 HOT 5
- how do we differ from Architecture and pattern? HOT 1
- Is this code a good way to update react component from dispatch in OOP class object? HOT 2
- Flux container breaks class methods defined with class properties syntax HOT 1
- Container create context issues after Babel 7.9.x update. HOT 5
- CVE-2020-15168 found in [email protected] HOT 13
- CVE-2020-7733 vulnerability by linking to an older fbjs version HOT 2
- Is fbemitter project discontinued? Is flux? HOT 1
- Add React 17 support
- NPM reports flux vulnerabilities HOT 3
- CVE-2021-27292: ua-parser-js needs to upgrade to 0.7.24 HOT 4
- mock flux store - react test HOT 2
- Will there be a Chinese version? HOT 1
- Video goes off screen and does not fit neatly HOT 1
- Add react-18 as peer dependency. HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from flux.