Home > Blockchain >  React with Recoil, large array is lagging because of many re-renders
React with Recoil, large array is lagging because of many re-renders

Time:07-03

I have an array of objects, around 400 items.

The component tree looks something like this

App
  -> Page (useRecoilState(ListAtom) for consumption)
    -> List 
      -> Item (useSetRecoilState(ListAtom) for mutation)
        -> Information
        -> Button

In the <Item> component, I display some <Information> which the user will look at and click the button to trigger something.

When the button is clicked, it makes a call to my backend, and upon success, the button will change its text.

This is all working as expected, but when the button re-renders, it causes a re-render for the entire tree.

I'm following this: https://recoiljs.org/docs/basic-tutorial/atoms/

To update an attribute of an item in the list, you need to replace that item with a new copy and return a completely new state, this will cause everything to re-render again, which takes a few seconds.

In the <Item> I'm using useSetRecoilState so I don't subscribe to changes as described here: https://recoiljs.org/docs/api-reference/core/useSetRecoilState

I was thinking that because these objects are referenced by memory, it would know not to re-render the non-mutated items, but looks like this isn't the case.

I might not be structuring my app or using Recoil correctly, but this seems to be what Recoil's tutorial is teaching devs to do, anyone got any ideas?

CodePudding user response:

You can suppress rerenders, which is a painstaking process if you didn't build your app with performance in mind. Or you can virtualize.

https://www.npmjs.com/package/react-intersection-observer

import React from 'react';
import { useInView } from 'react-intersection-observer';

const Item = () => {
  const { ref, inView, entry } = useInView({
    /* Optional options */
    threshold: 0,
  });

  if (!inView) return null

  return (
    <div ref={ref}>
      <h2>{`Header inside viewport ${inView}.`}</h2>
    </div>
  );
};

An early return null (or basic placeholder html) when the row is not visible is a quick win that might completely solve your performance problem

CodePudding user response:

Turns out there were two things I was doing wrong

  1. You need to use the AtomFamily utility, which essentially is a collection of Atoms that creates a dictionary. You give each atom a specific identifier so you can retrieve it by id. See https://recoiljs.org/docs/api-reference/utils/atomFamily

  2. I was using a selector that that .filter'd the original array of data, creating a completely new array, I thought I read somewhere that recoil will handle renders of children via the member reference of objects in the array, but I was wrong. I had to first filter out the raw list into separate atoms based on an attribute, then use those.

  • Related