I have an application which receives messages from a server every 1 second and via socket.io these messages are broadcast to a react component.
My react component is using a flux style architecture, so it calls an action when a message is received which adds (or updates) a record in a collection in a store.
My component monitors changes on the store and then updates the UI when it changes. In this case, it draws a marker on a map.
The problem is that due to the frequency of the updates my component redraws all markers every second, which I don't want.
I'm looking for an approach to allow my map component to respond to changes in a collection in a store, but not have the state update every second.
I thought I could have a collection for the raw data, and then only update the networkGPS collection selectively in my store, but the component seems to change based on any property in the store as they all seem to be part of its state.
In summary I'm looking to:
Collect data every 1 second in raw form and add this data to a store.
Bind a component to a collection in this store which I update only when changes require a UI redraw.
What I think I need to do:
Either:
Avoid putting the raw data into the state of the store (at the moment I'm unsure how to declare a collection which is not part of the state)
OR
Make my component more clever as to when it redraws, so it doesn't try to redraw on each state change.
I'm not sure which is the most appropriate solution for the problem, any info would be gratefully receieved.
Sorry if the question is a little unclear, I'm new to react / flux. Please do ask for any clarifications.
EDIT
At the moment I am getting round this issue by storing an array of the items (map markers in this example) I want my component to display in the component itself :(
When the state changes and render is called, I intercept the state properties, decide if anything needs to change in the collection of markers and only amend them if needed. If no changes are needed, the collection in the component stays the same and no redraw is performed.
This is not ideal, I feel like I should be able to allow my component to respond to only specific state changes but I've not found it yet, still looking for a better answer.
There are a couple of ways to approach this:
1) Use shouldComponentUpdate to compare old props and new props, re-rendering only the new marker states.
2) Switch to a cursor-based state tree that automatically triggers renders only for views that are bound to the specific parts of the state tree that have changed. I believe Baobab is one such library. See also Om (written in ClojureScript) and probably a number of other Flux implementations.
Given the impact of #2 my vote would be to go the route of #1.
I am not quite sure what's the problem that you face with updates every second.
However, the one place where you can queue the updates is between the socket.io and calling the action.
Instead of calling the action every second, you could queue the updates before sending it across to react.
Related
In my application I have a dialog window on which there are multiple input fields. What I want to do is to save user's input in the component's own state and only afterwards, say, inside "onClose" of the Dialog send the input to a redux store using "dispatch" function.
This way the dialog component would keep field data inside its own state.
The problem that I face is that I'm not sure what the best way is to rebuild dialog component state from information contained in the redux store.
If one refreshes the page with F5 or simply reloads it, then components lose their state and fields will appear blank, regardless of the fact that rehydrated redux store still contains valid input information.
The question is then, what is the best way to set components state from props? Moreover, doesn't it seem like an antipattern? What are some common techniques for such task?
One possibility is to set field values directly to those contained in "props". This would, however, imply that every small change of the input fields will result in copying and modifying redux store, which is slow & inefficient.
Usually building a state from props complicates code a lot, you have to map props both in constructor and getDerivedStatesFromProps.
I prefer to write component functions which return value based on passed props.
As you mentioned it may impact perfomance, to fix it you can use memoize-one library.
For more details you can check the following answer
I guess, this issue has been discussed before, both for the new RC router and the deprecated router. The favored solution seemed to be using services for passing data down to components activated by router-outlet.
However, the more I think about it, the more I find myself in a hell of concurreny, observables and eventemitters. Please consider the following scenario:
A route is called like this: "/car/123/motor/4/sparkcoil"
which means: get the data for the spark coil for the motor with ID 4, which, in turn, is part of a car with ID 123.
So, there are 3 components with nested routes, the "car" component, the "motor" component and the "spark coil" component. The first components have a router-outlet, in which the child component is rendered. Not too difficult, so far.
Let's make some assumptions:
1)
In the "motor" component I need access to to the car data, and in the "spark coil" component, I need to have access to both the car data and the motor data.
2) The backend call to get a car would not contain data about all of the available motor types, because of the immense data volume. So car, motor and spark coil data have to be fetched in separate backend calls.
The first component that is activated is for the route "/car/123" is the car component. It has a service injected, that makes the backend call and returns an Observable, to which the components subscribes.
Then, the second route "/motor/4" is activated. I need to know about the car object fetched by the parent component. Later on, the "/sparkcoil" component is activated, which needs to know about the motor and the car, as well. Obviously there is not way to get it passed by an #Input decorator, right now. Frequent recommendations are: use a service. Alright, so I inject service X into both the car and the motor component. Here comes the pain. I have no satisfying solution for designing the service without finding myself in a concurrent hell of eventemitters or observables.
Idea 1:
The car and the motor component call a function on the service named fetchMotor(id) and fetchCar(id) which, in turn, trigger the next() method of a motorFetchedEventEmitter and carFetchedEventEmitter. The motor and the sparkcoil component subscribe to all of the EventEmitters to get informed about when the backend data have been fetched. However, I don't know if the components subscribe to the EventEmitter before or after they fire. If before, everything is fine, if after, I need to need to "replay" the emittals (see Observable.replay(...)). Since the service and their EventEmitters are instantiated only once, replaying the Observables means that I may get a lot of (irrelevant) events.
Idea 2:
I'll do it the other way round. The car and the motor component remain idle until the spark coil component is called. This component resolves all route segments, performs the respective backend calls and propagates the backend response via EventEmitters of a service to the "car" and "motor" component. I admit that this approach is quite counterproductive in regards to the new component routing as of RC1.
Possibly there is a better solution for this scenario, considering life cycle hooks. According to the docs, the OnActivate interface (see https://angular.io/docs/js/latest/api/router/OnActivate-interface.html)
says, that when onActivate returns a promise, all child components won't be activated until the promise resolves. I don't think that there is a similar functionality for routed components.
Does anyone have a notion how to solve this as elegantly as possible.
Thanks for your ideas in advance. I need to get a coffee! :-?
You shouldn't use EventEmitter in services. This was common practice for quite some time but EventEmitter should only be used for #Output() on components.
I think the whole problem is just a design problem about how you use observables and services (I don't say its an easy problem to solve).
Using BehaviorService might be an option to fix timing issues where services subscribe to observables after these emitted a value. With BehaviorService a new subscriber immediately gets the last emitted value passed.
Your question doesn't contain enough information for more detailed suggestions and seems a bit too broad for StackOverflow anyway. If you can break down your problem to single issues and create new questions for these it might be easier to make concrete suggestions about how to fix it.
Suppose a complex component is subscribed to the redux store. The first time the component is mounted, we aggregate a lot of data from the store and create a "ModelView" object.
Then, imagine that this component receive an event to update itself because there's a new state. It seems like the render method would need to re-aggregate all the data from the store to know if there's a difference. I.e. we cannot do a simple comparison because the ModelView object is different than what's in the store.
You may ask "Why a ModelView", why not just read data from the store in render(). This is a fine approach for simple pages, but for complex pages the lag is noticeable and it is just not possible.
Also, a view may have an internal state that is different from the store. For instance, maybe a few tasks have been updated in the store, but we don't want to change the UI because that would be confusing for the user.. we'd rather show a "Click to see new change" button for instance. One approach to that is to store everything in the store.. both the ModelView AND the normal state.. but isn't that overkill? Or said differently, shouldn't the ModelView be stored inside the component itself?
Computing Derived Data is a recipe describing how a memoized “view model” can be derived in a composable way from the Redux store thanks with Reselect.
TL;DR edit in retrospect years later: there's no solution that's not gross as long as it's just state data - you'll need to also get it into a separate store somewhere somehow and can do whatever you want at that point. But read the question and the answer and the back-and-forth if you want some more background.
I have a table of two sections, each with various input values. Let's say that it is a survey. Feeding data into this is straightforward; I have the typical model:
{ "sections": [ { "name": "a", values: { "A": 1, "B": 2, "C": 1, ... } }, ... ], ... }
And a component hierarchy like:
<Survey>
<Section> (for each section)
<ValueRow> (for each value)
I put the model into a prop on the survey and the right information is trickled down into the subcomponents. Each ValueRow has a text field and its ephemeral value reflected back into its own state. This works fine "on the way down", in the one way flow that React is built for.
However, I also wish to show progress on the Section level and for the entire Survey, both simple things like number of fields filled out and statistical data needing the entire data set - what's the average across sections, how many "1" answers do I have in total, what's my grade (calculated from all the answers) and so on. Essentially, I'd also want to have:
<Survey>
<SurveyWideStats>
<Section> (for each section)
<SectionWideStats>
<ValueRow> (for each value)
This turns into a reduction of the current state instead of the model data. What's the best way of doing this in React? Flux and Actions and Stores all seem to deal with how to handle the data once it has been committed to the model. What I want to do is to pluck all the state data and do something with it, but it also seems terribly gross for the SurveyWideStats element, for example, to go poking through the garbagestate of its sibling element's children.
My current solution is to pass around an accumulation object and provide enough state to each component that it can keep calling that whenever something changes. This seems clear and divided enough, but it means that I have to have two passes and have to be careful not to start fiddling with state during rendering (at least since that's when I call the accumulation object - I suppose there may be a better point during the lifecycle where I could call that). And in addition, it seems like this would be an obstacle to "pick up from" server side rendering.
What's the best way? Is there an established pattern for this - preferably one where these things don't have to be so custom and really tailored to the data all the time?
Two ways to do this:
Pass the entire table as a prop to the highest component .
Inside survey's render function, calculate the stats, then pass them to the component as props, followed by the foreach loops over the table for the other children components. That way, your stats component is a pure component, does not need state and does not need to poke in siblings.
Create a stats function in a store, and have the component call this to get the stats. NB best not to save the stats in a store, since it is clearly derived data. Unless for performance reasons.
Hope this helps!
UPDATE:
To handle changes by the user when they change an input value, you have two options, depending on your preference:
(Option 1 describes a pure component).
(When you use flux pattern): Put the value of the input control in props. And whenever the user makes a change, fire an action to update a store, and have the store pass down updated props. So the (top) component notices a change event and rerenders. This creates more or less 'live' updates, e.g. when a user types a single character in an input field, the page title is updated immediately. The component with the input control does not have (and does not need) setState. This setup may become slow in really large component trees (because with each character, the entire tree is rerendered). But react is superfast and smartly only renders changes in de tree.
Put the initial prop value in state (in getInitialState() and put the input value in state also. Typical example: user types a character in an input field, the change triggers a setState() and the component is rendered again. Only when the user clicks some save or commit button, an action is fired to save the value in a store.
UPDATE:
As a bonus, below the flow for updating stores and components.
I'm still studying the Flux architecture, and noticed that:
one Action can cause multiple Stores to emit a "change" event
one ControllerView can be subscribed to the "change" event of multiple Stores
So, if ControllerView depends on data of two Stores, and those two Stores are both changed by one Action, the ContollerView - with all its components - will be rendered (to the virtual DOM) twice, the first time with incomplete data.
Is there any recognized pattern to avoid this? I can think of some simple solutions, but I wouldn't like to reinvent the wheel.
In general, you should just allow it to render more than once. However, the if the action always triggers actions in both stores you can use the "waitFor()" method of the dispatcher to let one store update first, then only emit a change when the second store gets updated.
This is only useful if the action will always affect both stores, however.
Best practice in flux pattern is to limit number of stateful components, and try only to have top component listen to stores, and send down all relevant info in props.
With multiple stores, one solution to minimize multiple renders after a single change:
create a StatStore that stores nothing, but listens to all relevant Actions, and waits for all other relevant stores.
this StatStore has getter functions, that collect (and possibly calculates stats) from other stores
your top component only listens to StatStore change emissions
top component then gets data from StatStore.
That way, a single change only results in one re-render.
I'll add this for future reference: I didn't find any good solution to this problem with vanilla Flux, and in the end switched to Redux, which has all the advantages of Flux and doesn't suffer from this disadvantage.