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
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/atomFamilyI 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 anattribute
, then use those.