React state with large collection of items - reactjs

I need to keep a large amount of items in state.
Consider
this.setState(oldState => ({
items: [...oldState.items ...newItems]
}))
It creates a new array and copies all the items here, which is slow.
The performant way of adding items would be pushing them on to the old list, but this requires mutating state directly, which may or may not lead to weird bugs.
Lately I moved onto using state like this:
this.setState({
["items"+num]: newItems
})
For example, if num==8, then we will create a new key in the dictionary "items8" and set its value to be equal to the array of new items.
I assume in this case React will mutate state to add a new key-value pair (as opposed to creating a new state object). Also, I assume React will pass a reference to the original array, rather than duplicating the array.
Is this a performant way of using React state with a large collection of items? For the purpose of this question, let's assume I am not rendering anything to the DOM.
Edit: I was asked for more context in the comments. I'm working on an open source infinite scroll implementation. It's not for any specific purpose. My data is images and my metadata contains only urls for the image and image thumbnail. However, someone who uses my infinite scroll implementation might use it for a different purpose, like blog posts, or videos, or any other types of items. The general idea is that as the user scrolls down, we fetch metadata for more items, and we want to add that metadata to state. So if someone scrolls very fast, they might cause many state changes per second, and I want those state changes to be fast even if there are many items in state.

Related

React Redux Store - Accessed by All Components

I am relatively new to React and Redux after spending a lot of time in the .net mvc and knockoutjs world. What I am having trouble understanding is how the store is used and how props are passed down between parent and child components.
My first question: if an array of values get sent down from parent to children components via props, do these components do a deep copy and create their own version of this array? If we have a rather large array of values that we want multiple children to have access to, and we send it as props, are we unnecessarily creating extra data or are they just simply referencing the same array?
I'm not sure exactly how the store is used, but could it be used to help the scenario above? If we have some values like a larger array that we want accessed by multiple component children, they could pull them from this globally accessible store?
I would appreciate if somebody could help me wrap my head around these concepts. In my scenario I'm working on we have a table of 30 students each of which gets sent an array of data to help fill in some rows. What I'm afraid we're doing though is giving each student a large amount of data that they don't need if they could pull from the parent or this global store.
<StudentTable
onSelectAllStudents={this.props.handleSelectAllStudents}
handleLoadStudentResponses={this.props.handleLoadStudentResponses}
structures={this.props.structures}
lessons={this.props.lessons}
activities={this.props.activities}
students={this.props.students} />
They are creating a table, where each row is a student. The table contains a large amount of data (which is a whole other problem) however what essentially we are doing is for each student we are showing whether they completed a particular activity or not. As you can see in the screenshot they build out a rather complex table. In the StudentTable they loop through students and create a Student component for each row which gets sent in the list of activities so that in the table they can color in a cell based on if the activity is published or not. My main fear was that with each Student component getting the list of activities it was adding a ton of data to memory as opposed to those numerous Student components just referencing some parent array.
Based on the syntax I've seen, children receive a pointer. Not a new object. Regarding your question about giving 30 students the entire array, you're not approaching data passing correctly. Show us a code snippet and I'll be able to comment more.
Also, redux is not helpful. It can be used, but it's overkill and adds needless complexity. Components with a child-parent relationship can very easily be handled with normal React prop passing. Redux is the go-to option if the components do not have any direct relation.
edit: When referencing elements in an obj, primitives are copied and objects are pointed.
const obj = {arrayObject: [], primitiveBoolean: true}
const primitive = obj.primitiveBoolean
const array = obj.arrayObject
primitive = false
console.log(obj.primitiveBoolean); //still true
array.push(10)
console.log(obj.arrayObject); // [10] push changed object's array

React redux: listing and editing. Should use one array of items or array of items and separate edittingItem?

If in my React app, utilizing Redux and redux-thunk, my store has an array of relatively lightweight/denormalized items that is used in a listing of said items (paginated in the API), but then I also have an edit/add option for each (which is the same item as in the listing array but with many additional fields, not de-normalized, etc.), I don't know which of the following is the best approach for storing in my store:
Maintain one array of items in my store, which contains all necessary data (normalized, and therefore each contains a relatively deep object graph). Editing simply requires finding it in the store, making the changes, and slicing back into the state.items array.
Maintain an array of minimalist items for the item listing, while also having a root state.currentItem object.
Maintain an array of minimalist items for the item listing, while resetting the array to contain only one item (containing all necessary data for editing). This avoids needing to create another reducer which may or may not be a good thing.
Because of the data-intensiveness of the actual listing item object, I have to go with option 2/3, but now I've got redundant data in two places that conceivably won't be in sync (although practically speaking, in my app, because of paging/sorting I must necessarily re-poll for latest data from the API upon returning back to the listing) or an array of items that can contain items in two different object formats.
Both feel dirty, which leads me to believe I'm missing something obvious that redux is already equipped to handle. Suggestions? Additional options?
Can the user update multiple items at the same time? Redux has 3 important principles to respect. One of them is the single source of truth, which means all valid/true data must come from one place (store here) and every data element is stored exactly once. Having multiple time the same items in the store is bad, mainly because of sync problems like you said. Also having deep objects is not a good approach, you should keep your state flat. Since you're using react with redux, you have access to state, the app state with redux and the component state with react. The most common pattern with this kind of problem is to create a copy of the object that you are editing in the component state. If the user cancels the editing, it will be easy to revert it. If the user stops editing the item without saving, you app state won't be affected. Also, you will avoid polluting your app state with wrong or duplicated data. Let's say that in your list when a user clicks on an item, the item transform to a text input. The list item will be base on the redux store item and the text input will be base on a copy of the same item. This also applies to new objects. You can also validate your item before adding/updating it. Keep in mind that the most important part is to keep your store clean because you will base you displayed items on that, so fetching new items won't affect your list. Doing this will also help you updating the item in the store because you will only have to replace it when dispatching a save action.

Why should I keep the state flat

I'm using ReactJs with Redux and on some tutorials and codes I see people suggesting and using normalizr to keep the state flat. But what is the real advantage in keeping it flat ? Will I encounter any problems if I don't ? Is it necessary ?
Three main reasons:
Updating nested Javascript objects immutably generally results in uglier code that is harder to maintain, unless you use a utility library to wrap up the process
Immutably updating nested data requires that you return new copies of all items in the nesting hierarchy. Since components generally do shallow-equality reference comparisons on data to see if they need to update, updating nested data usually means that more objects are updated, and more components will probably have to re-render even if the actual data isn't different.
Flat data, and in particular normalized data, enables some more optimized approaches for defining components (such as a list where each list item component is itself connected, given an item ID as a prop, and is responsible for looking up its own item's data by that ID)
I'm assuming that by keeping it flat, you mean not having nesting in your state object.
It is not advisable to have nesting in your state because you have to keep changing your state according to some events.
If you look at redux documentation, they want you to have pure reducers. And part of making your function pure is not modifying it's arguments.
When you have lots of nesting it's difficult to change state without inadvertently modifying the state object because all JS objects are passed by reference. When you have a lot of nesting you have to make deep copies of the state object before modifying it.

How to avoid React re-renders when using reselect to compute derived state

I'm using the ultimate combination of React + Redux + Reselect + Immutable.js in my application. I like the idea of reselect because it lets me keep my state (maintained by the reducers) as simple as possible. I use a selector to calculate the actual state I need which is then fed to the React components.
The problem here is that a small change in once of the reducers causes the selectors to recalculate the whole derived output and as the result also the whole React UI is updated. My pure components don't work. It's slow.
Typical example: The first part of my data comes from server and is basically immutable. The second part is maintained by the client and is mutated using the redux actions. They are maintained by separate reducers.
I use a selector to merge both parts into a single list of Records which is then passed to the React components. But obviously, when I change a single thing in one of the objects, the whole list is regenerated and new instances of Records is created. And the UI is completely re-rendered.
Obviously running the selector every time is not exactly efficient but is still reasonably fast and I'd be willing to make that trade off (because it does make the code way simpler and cleaner). The problem is the actual rendering which is slow.
What I'd need to do would be to deep merge the new selector output with the old one because Immutable.js library is smart enough not to create new instances when nothing was changed. But as selectors are simple functions that do not have access to previous outputs, I guess it's not possible.
I assume that my current approach is wrong and I'd like to hear other ideas.
Probably the way to go would be to get rid of reselect in this case and move the logic into a hierarchy of reducers that would use incremental updates to maintain the desired state.
I solved my problem but I guess there is no right answer as it really depends on a specific situation. In my case, I decided to go with this approach:
One of the challenges that the original selector handled nicely was that the final information was compiled from many pieces that were delivered in an arbitrary order. If I decided to build up the final information in my reducers incrementally, I'd have to make sure to count with all possible scenarios (all possible orders in which the information pieces could arrive) and define transformations between all possible states. Whereas with reselect, I can simply take what I currently have and make something out of it.
To keep this functionality, I decided to move the selector logic into a wrapping parent reducer.
Okay, let's say that I have three reducers, A, B and C, and corresponding selectors. Each handles one piece of information. The piece could be loaded from server or it could originate from the user on the client side. This would be my original selector:
const makeFinalState(a, b, c) => (new List(a)).map(item =>
new MyRecord({ ...item, ...(b[item.id] || {}), ...(c[item.id] || {}) });
export const finalSelector = createSelector(
[selectorA, selectorB, selectorC],
(a, b, c) => makeFinalState(a, b, c,));
(This is not the actual code but I hope it makes sense. Note that regardless of the order in which the contents of individual reducers become available, the selector will eventually generate the correct output.)
I hope my problem is clear now. In case the content of any of those reducers changes, the selector is recalculated from scratch, generating completely new instances of all records which eventually results in complete re-renders of React components.
My current solution looks lite this:
export default function finalReducer(state = new Map(), action) {
state = state
.update('a', a => aReducer(a, action))
.update('b', b => bReducer(b, action))
.update('c', c => cReducer(c, action));
switch (action.type) {
case HEAVY_ACTION_AFFECTING_A:
case HEAVY_ACTION_AFFECTING_B:
case HEAVY_ACTION_AFFECTING_C:
return state.update('final', final => (final || new List()).mergeDeep(
makeFinalState(state.get('a'), state.get('b'), state.get('c')));
case LIGHT_ACTION_AFFECTING_C:
const update = makeSmallIncrementalUpdate(state, action.payload);
return state.update('final', final => (final || new List()).mergeDeep(update))
}
}
export const finalSelector = state => state.final;
The core idea is this:
If something big happens (i.e. I get a huge chunk of data from the server), I rebuild the whole derived state.
If something small happens (i.e. users selects an item), I just make a quick incremental change, both in the original reducer and in the wrapping parent reducer (there is a certain duplicity, but it's necessary to achieve both consistency and good performance).
The main difference from the selector version is that I always merge the new state with the old one. The Immutable.js library is smart enough not to replace the old Record instances with the new Record instances if their content is completely the same. Therefore the original instances are kept and as a result corresponding pure components are not re-rendered.
Obviously, the deep merge is a costly operation so this won't work for really large data sets. But the truth is that this kind of operations is still fast compared to React re-renders and DOM operations. So this approach can be a nice compromise between performance and code readability/conciseness.
Final note: If it wasn't for those light actions handled separately, this approach would be essentially equivalent to replacing shallowEqual with deepEqual inside shouldComponentUpdate method of pure components.
This kind of scenario can often be solved by refactoring how the UI is connected to the state. Let's say you have a component displaying a list of items: instead of connecting it to the already built list of items, you could connect it to a simple list of ids, and connect each individual item to its record by id. This way, when a record changes, the list of ids itself doesn't change and only the corresponding connected component is re-rendered.
If in your case, if the record is assembled from different parts of the state, the selector yielding individual records could itself be connected to the relevant parts of the state for this particular record.
Now, about the use of immutable.js with reselect: this combination works best if the raw parts of your state are already immutable.js objects. This way you can take advantage of the fact that they use persistent data structures, and the default memoization function from reselect works best. You can always override this memoization function, but feeling that a selector should access its previous return value if often a sign that it is in charge of data that should be hold in the state / or that it is gathering too much data at once, and that maybe more granular selectors could help.
It looks like you are describing a scenario very close to the one why I wrote re-reselect.
re-reselect is a small reselect wrapper, which initializes selectors on the fly using a memoized factory.
(Disclaimer: I'm the author of re-reselect).

Calculating data dependent on non-committed (state) data of child/distant components in React

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.

Resources