colemars
colemars

Reputation: 1098

How to dynamically render list of components without re-rendering entire list with every new addition?

I want to render a list of components that have a 1:1 relationship with some objects in my redux store.

My current code is:

  props.images.forEach(imageFile => {
    let objectURL = URL.createObjectURL(imageFile);

    const newStyles = { 
      left: `${leftPosition}%`,
      top: `${topPosition}%`
    }

    previewFloats.push(
      <div className="Preview" style={{...style, ...newStyles}} key={v4()}></div>
    )
  });

  return(
    previewFloats
  )

Edit: Example here (codesandbox.io/s/brave-mcnulty-0rrjz)

The problem being that performance takes a pretty big hit rendering an entire list of background images as the list grows.

What is a better way to achieve this?

Edit: still unsure why background-image src is triggering a re-render, but the suggestion to use an <img> element instead has things working as intended.

Upvotes: 5

Views: 2526

Answers (3)

colemars
colemars

Reputation: 1098

So thanks to Dergash on Github I've discovered that the problem is not that my list is re-rendered.

Because I am creating a new URL every time with URL.createObjectURL inside of my component and because 'backgroundImage' does not do image prefetching whereas src attribute does I am seeing the flicker.

So my mistake was not creating the URL first and then passing it down into the component.

Working example: https://codesandbox.io/s/funny-http-8vrgv

More details: https://github.com/facebook/react/issues/16187

Upvotes: 0

Doğancan Arabacı
Doğancan Arabacı

Reputation: 3982

Your answer is with 'key' property of react components. It's built for such cases Assuming this is render part of your component, you should have something like this:

return this.props.images.map(imageFile => (
    <div className="Preview" key={imageFile}></div>
))

Assuming imageFile is a unique string. This way react will rerender your virtual dom but it'll recognise that there is no need to dom update for same elements with same key property

Upvotes: 2

GalAbra
GalAbra

Reputation: 5148

Array.map() allows dynamic rendering of elements in React, so that only those who change their props will re-render (as explained in the documentation).

If you'll use this method - rather than create the full array and render it fully - React will only re-render elements that are supposed to be updated:

props.images.map((imageFile, index) => {
    let objectURL = URL.createObjectURL(imageFile);

    const newStyles = { 
      left: `${leftPosition}%`,
      top: `${topPosition}%`
    }

    return (
      <div className="Preview" style={{...style, ...newStyles}} key={index}></div>
    )
});

Note that I've modified the key property of each element in the array. It should be unique and I didn't know what v4() is, so I just used the array indices.
React's documentation actually explains really well about these:

Keys help React identify which items have changed, are added, or are removed

Upvotes: 2

Related Questions