How to handle nested objects without re-render the whole app? #6842
-
https://codesandbox.io/p/github/vitormarkis/37react-query-best-practices-nested-objects/main App example: user has columns, columns has todos, each todo has a done checkbox that can be toggled on and off, basic stuff How should I manage the requests to this use-case? One request to fetch all data? or split the data fetch into individual requests for each column? At some point, the entry data will have to be refetched/mutated/optimistic updated, and this will change the entry data reference and trigger a re-render to my whole app. If I toggle one todo at column #85, all columns and all todos will be re-rendered. Is there a way to subscribe a column/todo to his slice of that big nested entry data object only? I'm rendering the columns like this The solution I came up with is to use React.memo, deep comparing the Is it right? I feel like I'm missing something here. I would like to put this single pieces of data together in one single root source, is easy to work this way. A Yes, I know re-render is not a bad thing but in my real use-case, I have a lot of "columns", each "column" have a lot of "todos", each "todo" has their own universe, their data, their modals, their dropdowns, their hooks... Re-render the whole list everytime a small change happened... If this is not a performance concern, I don't know what should be. I put together this small POC. You can see in the GIFs, the behavior with and without React memo. Without memowith memo, deep comparison on column props, and todo propsAlso, It seems like I tried to narrow down to a subset of the user, the actual column I'm rendering: // Column.tsx
const { data: column } = useQuery({
queryKey: ECacheKeys.user(userId),
structuralSharing: true,
select: user => user.columns.find(c => c.id === columnId)!,
}) But still, when Note: Column should not fetch any data, just use the data stored on queryProvider. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
Using export const ColumnList = React.forwardRef<React.ElementRef<"div">, ColumnListProps>(
function ColumnListComponent({ className, ...props }, ref) {
const { data: columns } = useUserQuery({ userId }, { select: u => u?.columns.map(c => c.id) })
if (!columns) {
return <div>Loading...</div>
}
return (
<div
{...props}
className={cn("flex flex-wrap gap-8 ", className)}
ref={ref}
>
{columns.map(column => (
<Column
columnId={column}
key={column}
/>
))}
</div>
)
},
) This gets it so that only the individual column re-renders toggling a todo, but you could potentially take this even further by moving more parts of your components into their own smaller components. Each of those could just take things like |
Beta Was this translation helpful? Give feedback.
Using
structuralSharing
andselect
is the right way to do this. The trick to prevent yourColumnList
from re-rendering is to also use theselect
there as the list doesn't care if any part of the column object change. It only cares if the array of column IDs change: