3

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.

Atte Juvonen
  • 4,922
  • 7
  • 46
  • 89
  • If you're not rendering anything to the dom why are you storing it in state? How slow is it? Can you show us the algorithm? – Train Mar 19 '19 at 16:21
  • @OrthoHomeDefense Of course I am actually rendering some items to the DOM, and I understand rendering items to the DOM is expensive. I wanted to ask about React state, not about rendering items to the DOM. – Atte Juvonen Mar 19 '19 at 16:22
  • @OrthoHomeDefense Old version of it is here: https://gatsby-starter-infinite-scroll.baobab.fi/ That version renders all the items to the DOM, which becomes expensive when you have thousands of items. I will change it so that only a "window" of items is rendered to the DOM. I wanted to ask about using state efficiently. – Atte Juvonen Mar 19 '19 at 16:25
  • Gotcha :). So your data sets are extremely large. You should use state with lazy loading. I would break up your huge data set into smaller ones as you load the data. This way you're not mutating an extremely large data set. – Train Mar 19 '19 at 16:31
  • @OrthoHomeDefense Yeah, if you open the network tab you will find that I'm already doing that :) So the question is: when the user scrolls down and we fetch "page 277" of metadata, how do we save that metadata to state efficiently? – Atte Juvonen Mar 19 '19 at 16:35
  • I would probably do 1 of 2 things. 1. I would break up the large data set into smaller ones of around a size of roughly 24-50 elements and have a bunch of small data sets, and continually add small data sets to state. Or 2. since the user can only see a limited amount of images, I would only have a small array with the visible images of ~24-50 elements in state. – Train Mar 19 '19 at 16:53
  • Can you provide more information about what your data set looks like? What is the initial shape of your data? What are the situations that cause the data to change? How are you expecting state to update? Knowing more about the shape of the data will help us answer the question and help find a more efficient way. – Galupuf Mar 19 '19 at 16:59
  • @OrthoHomeDefense I have already implemented point 1: the dataset is already paginated into pages of roughly 24-50 elements, and I am continually adding those small pages into state. I wanted to ask about what is an efficient way of doing those state changes. With regards to point 2: I will definitely implement a "window" for elements that should be rendered to the DOM. – Atte Juvonen Mar 19 '19 at 17:01
  • @Galupuf I added answers to your questions into the OP. – Atte Juvonen Mar 19 '19 at 17:07
  • 2
    Look into [react-virtualized](https://github.com/bvaughn/react-virtualized) or [react-window](https://github.com/bvaughn/react-window). react-window seems to be more lightweight and newer – Cory Danielson Mar 19 '19 at 17:15
  • Since it's so dynamic I would go more granular on fetching data. I would keep it as is fetching only base image or url data for scrolling. That looks like it works just fine. Then I would fetch the other dynamic data after you click on an image and display that component. I wouldn't save all the dynamic data in state since you don't know how large it could be. The user can only click on one section at a time so only keep the state of one "window" and just do a fetch every time someone clicks on something. I doubt it's going to be efficient if you store all that dynamic data in there. – Train Mar 19 '19 at 17:18
  • 2
    What you're doing in state is an efficient approach to storing the data. You're not duplicating data, and it's indexed in a predictable fashion. It seems like you're rendering items with a fixed/known height so you'd be able to accurate retrieve the items that you need to render based on scroll position. Imo, the dynamically named keys are unnecessary `"items"+num`, I would just got with `[num]: newItems` – Cory Danielson Mar 19 '19 at 17:20
  • @OrthoHomeDefense Yeah absolutely, I don't intend to add actual item data to state, only metadata (e.g. url). I'm asking about how to add item metadata to state efficiently. – Atte Juvonen Mar 19 '19 at 17:20
  • @CoryDanielson Almost. It's a responsive CSS Grid so if you resize the window, image size will change. But we can set up a throttled listener for the resizer and then ask the browser what size the rendered items are and how many there are per row. – Atte Juvonen Mar 19 '19 at 17:24
  • are you sure populating array itself may be a bottle neck? it looks to be really [fast operational](https://stackoverflow.com/questions/3978492/fastest-way-to-duplicate-an-array-in-javascript-slice-vs-for-loop) comparing to anything happens later like XHR response transformation, possible duplicate detection, conciliation or DOM rendering(even if your code does not do it itself it will happen anyway) – skyboyer Mar 19 '19 at 18:49
  • @skyboyer You are right things like what we render to DOM is more important in terms of performance. In any case, I wanted to ask about state. – Atte Juvonen Mar 19 '19 at 19:02
  • sorry if it seemed like "you should not ask", actually it was "you don't need to care". but sure you still can :) – skyboyer Mar 19 '19 at 19:29

0 Answers0