How to manage state in a Flux/ReactJS app with Stores? - reactjs-flux

In a FLUX/ReactJS arhitecture, I am curious if the following approach violates (or is just not recommended) the FLUX flow.
1) We have 2 stores. Store A & Store B.
2) We have one "App" Component which has its State set from Store A and passes it down to Component 1 and Component 2.
3) Component 1 uses the data from the received "this.props" and renders stuff.
4) Component 2 uses data from the received "this.props" but also has its own state based on Store B (the same way "App Component" has its state).
From what I understand - ideally - I would make the "App Component" listen to both Store A & Store B and pass everything down to the other components.
However, in a real life application, you'd have let's say 100 stores, each with its own conditions (you could say Component 2 doesn't get rendered if a certain combo of data is not met, etc). That would make the App Component a GOD-like component which handles so much stuff. Impractical in my mind.
In my mind, you still get a one-direction flow of data even if you don't have a top component managing all state and passing it down to the components - since the state is still dictated by the Stores, not by the components themselves (and you trigger events via Actions->Dispatcher->Store). This is especially good in my mind if you want to encapsulate a certain behavior in a component.
Imagine the following scenario:
AppComponent -> AuthComponent -> LoginFormComponent
AppComponent -> ListItemsComponent -> SingleItemComponent
Wouldn't it be weird if AppComponent knew about the "AuthStore" state, just so it can pass it to the AuthComponent via props ?
Wouldn't it be better if the AppComponent knew nothing (in this example) and just renderered the 2 children;
The AuthComponent would listen to the AuthStore and pass down info to the
LoginForm;
The ListItemsComponent would listen to a ListItemsStore and pass down the needed info to SIngleItemComponent etc..
Which approach would you guys take?

Theoretically If your lower level component is completely isolated and there is 0% chance that, depending on its state, you would need to change the state of higher level (or the same level) components, then there is nothing wrong with managing its state within the component itself.
There are downsides however:
If later you decide to use the state of the component higher in the
hierarchy you'd need to refactor.
It makes a component less
reusable. You'd need to have same store, same actions etc. whereas if you received state via props you could plug it into other project much more easily.
It makes it harder to test.
I would say that if it's a large independent component, with many lower components it makes sense to keep the state within it, but usually there is only one such component in the app. If it's more of a small, one purpose component, there is no point in doing this.

Related

What is the best practice to get the required data into VueJS component?

I'm building my first VueJS application which is intended to be used by hundreds of people in the future. I tried to make the individual components reusable and indpendent as possible. To achieve this i decided to let every component fetch its required data themselves. This works fine but i'm not sure if its best practice. I could also pass the data between the components or even using the 2-way data binding functionality.
The sketch bellow describes one of the situations i have. Note that 1 account has 1..* users. As you can see i need to fetch the accounts to display them in the accountOverviewComponent.
Currently i only fetch the accounts in the accountOverviewComponent and fetch the users when the account edit button by the passed accountId in the accountOverviewComponent is clicked. This way i don't fetch data i don't need at the time.
I can also include the users (god knows which data/relations will be added in future) to the fetch account response as wel so i can pass all required data to the accountShowComponent when a account edit button is clicked. This way i can save requests to the server with the side note that i fetch users of accounts i dont need. A possible disadvantage is that the account is updated in the accountShowComponent and not in the accountOverviewComponent (for example when the accountShowComponent is a modal on top of the accountOverviewComponent. So i need to pass the updatet account back or re-fetch the accounts after a save or something.
As third option I can do the same in option 2 but than with the 2-way data binding which handles the data synchronization between the components. This will probably restrict the usage of the accountShowComponent to cases where the accountShowComponent is used "on top" of a parent which contains the data.
I can also store the data in a Vuex store and update the stores all the time. I read that this is bad practive as it should be only used for data which is required accros the SPA. I think Vuex is overkill in "simple" situations like this?
What is the best practice of the described situation? I have a bunch of comparable situations/scenarios in my application. Performance (also for mobile devices), scalability and being "future proof" (extendability/modularity) are important for me. Can someone help me out because i'm a bit lost in the options i have?
UPDATE
The reason i think Vue is overkill is comming from this article which makes totally sense from a software engineer perspective to me (i may be wrong). As my components have a kind of "parent - child" relation so i can solve my "issue" easily with passing data (or use 2-way data binding) and callback-events.
The number one use case for storing data in a centralized store like Vuex, is, because the data must be accessible in multiple places of your application, by components which oftentimes are not related in any way (they neither are parents or children of each other). An example of this would be certain user settings to configure how your application looks or what date format should be used, to name a concrete example.

Can a React-Redux app really scale as well as, say Backbone? Even with reselect. On mobile

In Redux, every change to the store triggers a notify on all connected components. This makes things very simple for the developer, but what if you have an application with N connected components, and N is very large?
Every change to the store, even if unrelated to the component, still runs a shouldComponentUpdate with a simple === test on the reselected paths of the store. That's fast, right? Sure, maybe once. But N times, for every change? This fundamental change in design makes me question the true scalability of Redux.
As a further optimization, one can batch all notify calls using _.debounce. Even so, having N === tests for every store change and handling other logic, for example view logic, seems like a means to an end.
I'm working on a health & fitness social mobile-web hybrid application with millions of users and am transitioning from Backbone to Redux. In this application, a user is presented with a swipeable interface that allows them to navigate between different stacks of views, similar to Snapchat, except each stack has infinite depth. In the most popular type of view, an endless scroller efficiently handles the loading, rendering, attaching, and detaching of feed items, like a post. For an engaged user, it is not uncommon to scroll through hundreds or thousands of posts, then enter a user's feed, then another user's feed, etc. Even with heavy optimization, the number of connected components can get very large.
Now on the other hand, Backbone's design allows every view to listen precisely to the models that affect it, reducing N to a constant.
Am I missing something, or is Redux fundamentally flawed for a large app?
This is not a problem inherent to Redux IMHO.
By the way, instead of trying to render 100k components at the same time, you should try to fake it with a lib like react-infinite or something similar, and only render the visible (or close to be) items of your list. Even if you succeed to render and update a 100k list, it's still not performant and it takes a lot of memory. Here are some LinkedIn advices
This anwser will consider that you still try to render 100k updatable items in your DOM, and that you don't want 100k listeners (store.subscribe()) to be called on every single change.
2 schools
When developing an UI app in a functional way, you basically have 2 choices:
Always render from the very top
It works well but involves more boilerplate. It's not exactly the suggested Redux way but is achievable, with some drawbacks. Notice that even if you manage to have a single redux connection, you still have have to call a lot of shouldComponentUpdate in many places. If you have an infinite stack of views (like a recursion), you will have to render as virtual dom all the intermediate views as well and shouldComponentUpdate will be called on many of them. So this is not really more efficient even if you have a single connect.
If you don't plan to use the React lifecycle methods but only use pure render functions, then you should probably consider other similar options that will only focus on that job, like deku (which can be used with Redux)
In my own experience doing so with React is not performant enough on older mobile devices (like my Nexus4), particularly if you link text inputs to your atom state.
Connecting data to child components
This is what react-redux suggests by using connect. So when the state change and it's only related to a deeper child, you only render that child and do not have to render top-level components everytime like the context providers (redux/intl/custom...) nor the main app layout. You also avoid calling shouldComponentUpdate on other childs because it's already baked into the listener. Calling a lot of very fast listeners is probably faster than rendering everytime intermediate react components, and it also permits to reduce a lot of props-passing boilerplate so for me it makes sense when used with React.
Also notice that identity comparison is very fast and you can do a lot of them easily on every change. Remember Angular's dirty checking: some people did manage to build real apps with that! And identity comparison is much faster.
Understanding your problem
I'm not sure to understand all your problem perfectly but I understand that you have views with like 100k items in it and you wonder if you should use connect with all those 100k items because calling 100k listeners on every single change seems costly.
This problem seems inherent to the nature of doing functional programming with the UI: the list was updated, so you have to re-render the list, but unfortunatly it is a very long list and it seems unefficient... With Backbone you could hack something to only render the child. Even if you render that child with React you would trigger the rendering in an imperative way instead of just declaring "when the list changes, re-render it".
Solving your problem
Obviously connecting the 100k list items seems convenient but is not performant because of calling 100k react-redux listeners, even if they are fast.
Now if you connect the big list of 100k items instead of each items individually, you only call a single react-redux listener, and then have to render that list in an efficient way.
Naive solution
Iterating over the 100k items to render them, leading to 99999 items returning false in shouldComponentUpdate and a single one re-rendering:
list.map(item => this.renderItem(item))
Performant solution 1: custom connect + store enhancer
The connect method of React-Redux is just a Higher-Order Component (HOC) that injects the data into the wrapped component. To do so, it registers a store.subscribe(...) listener for every connected component.
If you want to connect 100k items of a single list, it is a critical path of your app that is worth optimizing. Instead of using the default connect you could build your own one.
Store enhancer
Expose an additional method store.subscribeItem(itemId,listener)
Wrap dispatch so that whenever an action related to an item is dispatched, you call the registered listener(s) of that item.
A good source of inspiration for this implementation can be redux-batched-subscribe.
Custom connect
Create a Higher-Order component with an API like:
Item = connectItem(Item)
The HOC can expect an itemId property. It can use the Redux enhanced store from the React context and then register its listener: store.subscribeItem(itemId,callback). The source code of the original connect can serve as base inspiration.
The HOC will only trigger a re-rendering if the item changes
Related answer: https://stackoverflow.com/a/34991164/82609
Related react-redux issue: https://github.com/rackt/react-redux/issues/269
Performant solution 2: listening for events inside child components
It can also be possible to listen to Redux actions directly in components, using redux-dispatch-subscribe or something similar, so that after first list render, you listen for updates directly into the item component and override the original data of the parent list.
class MyItemComponent extends Component {
state = {
itemUpdated: undefined, // Will store the local
};
componentDidMount() {
this.unsubscribe = this.props.store.addDispatchListener(action => {
const isItemUpdate = action.type === "MY_ITEM_UPDATED" && action.payload.item.id === this.props.itemId;
if (isItemUpdate) {
this.setState({itemUpdated: action.payload.item})
}
})
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
// Initially use the data provided by the parent, but once it's updated by some event, use the updated data
const item = this.state.itemUpdated || this.props.item;
return (
<div>
{...}
</div>
);
}
}
In this case redux-dispatch-subscribe may not be very performant as you would still create 100k subscriptions. You'd rather build your own optimized middleware similar to redux-dispatch-subscribe with an API like store.listenForItemChanges(itemId), storing the item listeners as a map for fast lookup of the correct listeners to run...
Performant solution 3: vector tries
A more performant approach would consider using a persistent data structure like a vector trie:
If you represent your 100k items list as a trie, each intermediate node has the possibility to short-circuit the rendering sooner, which permits to avoid a lot of shouldComponentUpdate in childs.
This technique can be used with ImmutableJS and you can find some experiments I did with ImmutableJS: React performance: rendering big list with PureRenderMixin
It has drawbacks however as the libs like ImmutableJs do not yet expose public/stable APIs to do that (issue), and my solution pollutes the DOM with some useless intermediate <span> nodes (issue).
Here is a JsFiddle that demonstrates how a ImmutableJS list of 100k items can be rendered efficiently. The initial rendering is quite long (but I guess you don't initialize your app with 100k items!) but after you can notice that each update only lead to a small amount of shouldComponentUpdate. In my example I only update the first item every second, and you notice even if the list has 100k items, it only requires something like 110 calls to shouldComponentUpdate which is much more acceptable! :)
Edit: it seems ImmutableJS is not so great to preserve its immutable structure on some operations, like inserting/deleting items at a random index. Here is a JsFiddle that demonstrates the performance you can expect according to the operation on the list. Surprisingly, if you want to append many items at the end of a large list, calling list.push(value) many times seems to preserve much more the tree structure than calling list.concat(values).
By the way, it is documented that the List is efficient when modifying the edges. I don't think these bad performances on adding/removing at a given index are related to my technique but rather related to the underlying ImmutableJs List implementation.
Lists implement Deque, with efficient addition and removal from both the end (push, pop) and beginning (unshift, shift).
This may be a more general answer than you're looking for, but broadly speaking:
The recommendation from the Redux docs is to connect React components fairly high in the component hierarchy. See this section.. This keeps the number of connections manageable, and you can then just pass updated props into the child components.
Part of the power and scalability of React comes from avoiding rendering of invisible components. For example instead of setting an invisible class on a DOM element, in React we just don't render the component at all. Rerendering of components that haven't changed isn't a problem at all as well, since the virtual DOM diffing process optimizes the low level DOM interactions.

Flux- infinite scroll: i cannot think of how to stick to unidirectional data flow

i am trying to implement an infinite scroll from many items that i get from the server, but i cannot find any proper way to keep the flux architecture design rules.
the idea is: on the first load, i get a full item list from server (only id's), then using ajax i fetch each time 20 more items.
the list is kept in the Store, and also the loaded items. the view listens on loaded items and render them, when it reaches scroll bottom it calls an action which should then fetch 20 more items, and so on.
the problem is: the Action should know what items to fetch, the unloaded items list is in the store, so it has to get it from the store directly, which is a "don't do it' in flux. other alternatives are to handle all the logic in the stores, which seems also a bad idea..
can anyone think of a nice solution?
UPDATE: it is OK within unidirectional flow for a component to read directly from store (see below)
Make your action explicitly say which items to fetch: "Give me items 21-40 please".
This fires a) (async) ajax call to get items 21-40 and b) dispatch to the store.
The component knows a) which items it has already rendered, and b) which items the user wants to see next, so it can pass along the above action message without talking to the store again.
The store receives the request. The store knows it does not have the items yet. The component does not know yet.
Store emits change, and your component (assuming it is listening to store changes) gets current state from store. If the items weren't there, the store provides a loading state ("loading items 21-40" or similar). The component displays the loading state. (or, if the loaded items are already fully in store, it simply renders items 21-40).
As soon as items 21-40 are delivered by ajax return, your store updates with the full items 21-40. (if they happened to be in store already, no problem, no update). Store emits another change. Component hears this, and re-renders.
ASIDE:
Unidirectional flow is for updates:
Component -> lower components -> actions (-> webAPI -> action) -> dispatcher -> stores -> components
In unidirectional flow rules are:
Components are allowed to push data updates only to lower components (by passing new props, which trigger re-render), not to higher components
Components are allowed to maintain an internal state, which they can pass on as props to children (see 1)
Components are allowed to push data updates or update requests also to the dispatcher (in "actions"). The dispatcher then forwards the updates to the stores and/or to some server via eg webAPI.
Components are allowed to listen to store changes and pull/ read data directly from the store.
Stores listen to the dispatcher, and update if they receive news from the dispatcher.
Stores may also listen to other stores, and read data from other stores to update themselves
Stores emit change as soon as they have updated, so that any components listening can do something (typically read new data) (see 4.)
WebAPI results from the server are "actions". They go through dispatcher which informs the relevant stores to update. (See 5)
Unidirectional flow breaks if:
Component actively fetches/ pulls data from a higher component - such data should be pushed by higher component as props (see 1)
Component actively fetches data from child - as parent, component should already have this data. If it is in child's state, then state is designed at too low level.
Component directly updates store - should be with an action through dispatcher
And also breaks if (although some disagree):
Store directly updates another store - should be pull instead of push (see 6)
Store pushes update through an action - only webAPI (see 8) and components (see 3) are allowed to issue actions
Component directly does webAPI request and handles result in state - should go through dispatcher

How to handle data composition and retrieval with dependencies in Flux?

I'm trying to figure out what is the best way to handle a quite commons situation in medium complex apps using Flux architecture, how to retrieve data from the server when the models that compose the data have dependencies between them. For example:
An shop web app, has the following models:
Carts (the user can have multiple carts)
Vendors
Products
For each of the models there is an Store associated (CartsStore, VendorsStore, ProductsStore).
Assuming there are too many products and vendors to keep them always loaded, my problem comes when I want to show the list of carts.
I have a hierarchy of React.js components:
CartList.jsx
Cart.jsx
CartItem.jsx
The CartList component is the one who retrieves all the data from the Stores and creates the list of Cart components passing the specific dependencies for each of them. (Carts, Vendors, Products)
Now, if I knew beforehand which products and vendors I needed I would just launch all three requests to the server and use waitFor in the Stores to synch the data if needed. The problem is that until I get the carts and I don't know which vendors or products I need to request to the server.
My current solution is to handle this in the CartList component, in getState I get the Carts, Vendors and Products from each of the Stores, and on _onChange I do the whole flow:
This works for now, but there a few things I don't like:
1) The flow seems a bit brittle to me, specially because the component is listening to 3 stores but there is only entry point to trigger "something has changed in the data event", so I'm not able to distinguish what exactly has changed and react properly.
2) When the component is triggering some of the nested dependencies, it cannot create any action, because is in the _onChange method, which is considering as still handling the previous action. Flux doesn't like that and triggers an "Cannot dispatch in the middle of a dispatch.", which means that I cannot trigger any action until the whole process is finished.
3) Because of the only entry point is quite tricky to react to errors.
So, an alternative solution I'm thinking about is to have the "model composition" logic in the call to the API, having a wrapper model (CartList) that contains all 3 models needed, and storing that on a Store, which would only be notified when the whole object is assembled. The problem with that is to react to changes in one of the sub models coming from outside.
Has anyone figured out a nice way to handle data composition situations?
Not sure if it's possible in your application, or the right way, but I had a similar scenario and we ended up doing a pseudo implementation of Relay/GraphQL that basically gives you the whole tree on each request. If there's lots of data, it can be hard, but we just figured out the dependencies etc on the server side, and then returned it in a nice hierarchical format so the React components had everything they needed up to the level where the call came from.
Like I said, depending on details this might not be feasible, but we found it a lot easier to sort out these dependencies server-side with stuff like SQL/Java available rather than, like you mentioned, making lots of async calls and messing with the stores.

How to avoid action chains

I'm trying to understand Flux pattern.
I believe that in any good design the app should consist of relatively independent and universal (and thus reusable) components glued together by specific application logic.
In Flux there are domain-specific Stores encapsulating data and domain logic. These could be possibly reused in another application for the same domain.
I assume there should also be application-specific Store(s) holding app state and logic. This is the glue.
Now, I try to apply this to imaginary "GPS Tracker" app:
...
When a user clicks [Stop Tracking] button, corresponding ViewController raises STOP_CLICK.
AppState.on(STOP_CLICK):
dispatch(STOP_GEOLOCATION)
dispatch(STOP_TRACKING)
GeolocationService.on(STOP_GEOLOCATION):
stopGPS(); this.on = false; emit('change')
TrackStore.on(STOP_TRACKING):
saveTrack(); calcStatistics(); this.tracking = false; emit('change')
dispatch(START_UPLOAD)
So, I've got an event snowball.
It is said that in Flux one Action should not raise another.
But I do not understand how this could be done.
I think user actions can't go directly to domain Stores as these should be UI-agnostic.
Rather, AppState (or wherever the app logic lives) should translate user actions into domain actions.
How to redesign this the Flux way?
Where should application logic go?
Is that correct to try to keep domain Stores independent of the app logic?
Where is the place for "services"?
Thank you.
All of the application logic should live in the stores. They decide how they should respond to a particular action, if at all.
Stores have no setters. The only way into the stores is via a dispatched action, through the callback the store registered with the dispatcher.
Actions are not setters. Try not to think of them as such. Actions should simply report on something that happened in the real world: the user interacted with the UI in a certain way, the server responded in a certain way, etc.
This looks a lot like setter-thinking to me:
dispatch(STOP_GEOLOCATION)
dispatch(STOP_TRACKING)
Instead, dispatch the thing that actually happened: STOP_TRACKING_BUTTON_CLICKED (or TRACKING_STOPPED, if you want to be UI-agnostic). And then let the stores figure out what to do about it. All the stores will receive that action, and they can all respond to it, if needed. The code you have responding to two different actions should be responding to the same action.
Often, when we find that we want dispatch within a dispatch, we simply need to back up to the original thing that happened and make the entire application respond to that.

Resources