I'm pretty new to React / Redux, but love to play with it so far.
I came accros something that actually bothers me, but I'm not even sure it's a "real" issue I should care about. Anyway, here it is.
Let's say I have a component, which is actually a form for updating a record, retrieved from a database. This record has several foreign key to different tables. (Let's say my main table is Training, which has a Format, a Type, a Place... and all those data come from another tables).
In order to be able to display all the possible values for each of the foreign key to the user, I have to request all the different tables to get the data, and display those in dropdowns.
For now, I'm doing something like :
dispatch(new CrudActions(places).getAll());
dispatch(new CrudActions(trainingFormats).getAll());
dispatch(new CrudActions(trainingTypes).getAll());
Each of these line will dispatch a redux action, and so, update a part of the state, according to the data that is retrieved.
Then my component will then simply get the values from state :
function mapStateToProps(state) {
return {
places: state.places.list,
trainingFormats: state.trainingFormats.list,
trainingTypes: state.trainingTypes.list
}
}
It's actually working, but the consequence is : each time an action finishes and updates the state, my component get re-rendered... Let's imagine my main training has 10 foreign keys : for a single page load to display the update form, my component will be rendered 10 times.
Wouldn't it cause bad performances ? Is there any better way to retrieve foreign data ?
Let me rephrase that. Each of your record components has dropdowns for places, training format and training type. The dropdown options are retrieved via ajax. When you have several records, there will be a lot of requests and rerenderings.
The solution: Don't let every record component retrieve the dropdown values on its own. For each respective dropdown, they are all the same anyway. Instead load them in one of the parent components and pass them to the record components as properties, for example as availablePlaces, availableFormats, availableTypes.
Your parent component does not even have to load the available dropdown options via ajax. It can be initialized with it.
For further optimizations concerning rerendering have a look at https://facebook.github.io/react/docs/react-component.html#shouldcomponentupdate.
Facebook recommends making ajax calls in https://facebook.github.io/react/docs/react-component.html#componentdidupdate.
Related
Not so long ago, I started working with react, and I have a performance problem.
I use react + redux + reselect + immutable.js. I have a lot data (like big table with data ~10mb).
My problem is: when I have ~10 000 rows, react creates 10 000 components with: individual connect, memoize selector.
1 row consist of ~50 keys (from different stores, for ex: col height, focus index), arrays and etc. For example key title.
When I modify store, for example change title in row #123, redux will execute 10 000 memoize selectors, compare results and it take ~1-2 sec for 10k rows!
How I can prevent connect calls when I know which component must exactly be re-rendered? Like signal "component with id: row-123 must run memoize selector and check changes" or any ideas?
I plan to try mobx but I don't actually sure that mobx will prevent unnecessary data comparing for every of 10k component.
P.S. I already thought about pagination, virtual scrolling, but I need to display all the data at the same time into the DOM
As long as your app state is updated, each connected component will compare new and old state and properties (one by one) to understand whether a new component render is needed.
This operation is also supposed to trigger the selectors used to derive connected components' properties.
Redux's connect API exposes the possibility of customising the pre-rendering checks providing an options object with the following custom checks:
areStatesEqual
areOwnPropsEqual
areStatePropsEqual
areMergedPropsEqual
I really wanted to comment but I cant, sorry for that. But I think "react-virtualized" can be what you are looking for
https://bvaughn.github.io/react-virtualized/#/components/List
I have a app in which users can build a "Quote". So far I've been adhering to "idiomatic" redux (or something close to it) and it's been working out well.
However, I'm struggling to deal with a fairly simple scenario:
When the page is first opened, I fire an async LOAD event which retrieves info from the server needed to build the quote (products, inventory, previously saved line items, etc).
I need some way to be able to automatically add a specific line item(s) to the quote first it's first opened.
I've added a defaultLineItems property to my LOAD payload, but to fire the addLineItem(product, inventory, options) action, I need data from the productReducer, inventoryReducer, optionsReducer. The lineItemReducer could look at these defaultLineItems and try to set it's state appropriately, but that would require having to rewrite a lot of BL typically handled by the actions using data aggregated from reducer memorized "selectors" (EG: defaulting price, quantity, currency translation, etc)
I can think of a couple ways to achieve this, but they all seem somewhat hack-ish (IE storing a flag in the reducer that says I need to fire an action and then running it when my root component props update). It seems like a react component should not be responsible for this type thing.
What is the proper way to do something like this?
Seems there are a couple different ways this can be accomplished but for me the most balanced approach between simplicity and design was to use store.subscribe in conjunction with a reducer to track the last action(s).
At it's simplest, this would look something like this.
store.subscribe(function() {
let state = store.getState();
if(state.lastAction.type === ActionKeys.LOAD){
console.log('load action fired!');
}
})
Please be aware that firing an action from store.subscribe will cause recursion so you need to be selective.
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.
The scenario is this:
I have a form with dynamic (meaning they are not present on the initial render of the form) field sets.
So the main form would have fields for first_name, last_name, phone, etc and it would also have the possibility to add "rooms with items" like for example a bedroom with 2 chairs and a table.
I would like to store (to localStorage) and restore the state of the entire form (including it's dynamic components) but I'm having a hard time figuring out a way to easily do that.
I've tried storing everything in the main form's state but that becomes a nightmare to manage.
Is there anything that React provides for that purpose? Something that would allow me to take a snapshot and restore it later?
Thank you
What do you use with React ? If you were using redux I would suggest using redux form http://erikras.github.io/redux-form/#/examples/dynamic?_k=5pwhl7 - you would be able to easily serialise/deserialise the form state to be persisted.
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.