Skip to content

Structural sharing prevents a re-render after adding a key to an object with setQueryData (React Native) #6812

Closed as not planned
@ngokevin

Description

@ngokevin

Describe the bug

Structural sharing causes issues where when I use setQueryData to pass in completely new data, my useQuery hooks does not rerender. It does return updated data, just doesn't re-render and useEffects don't catch the data updates. Once I turn off structural sharing, everything works fine.

I have a hook that returns a map:

export function usePacks() {
  const { data } = useQuery<Record<string, Pack>>(
    ['packs'],
    fetchPacks,
    {
      staleTime: 1000 * 60,
      cacheTime: Infinity,
    });
    
    return data;
  }

Then I have a function that mutates this data, deep cloning to ensure no referential equality:

function updateCache (queryClient: QueryClient, newPack: Pack) {
  const data = queryClient.getQueryData(['packs']);
  data[newPack.id] = newPack;
  queryClient.setQueryData(['packs'], JSON.parse(JSON.stringify(data)));
}

This is where it gets weird. usePacks does not react at all to this data in terms of a re-render. usePacks I believe will return the updated data, but it won't re-render, and useEffects won't trigger:

function Component () {
  const packs = usePacks();
  
  useEffect(function () {
    console.log('Does not trigger when it should');
  }, [packs]);
  
  useEffect(function () {
    updateCache(queryClient, { id: 'bar' });
  }, []);
  
  return (
    <!-- This actually renders the newest data. -->
    <View>
      {Object.values(packs).map(p => <Text>{p.id}</Text>)}
    </View>
  )
}

Sure it renders the data here as a result of other things triggering a re-render. But on other screens where I am using the hook (e.g., on other screens down the react-navigation stack), it doesn't re-render and shows old data.

It Works if I Turn Off structuralSharing

The instant I turn off structural sharing, re-renders trigger as expected.

Your minimal, reproducible example

I added a Code Sandbox but I was unable to reproduce the issue in that environment. The code does show how it's set up in my codebase. I'm hoping this triggers some ideas from you

https://codesandbox.io/p/devbox/adoring-kepler-z93j2c

Steps to reproduce

  1. Have a useQuery that returns an object
  2. Use setQueryData to add a key to that object

Expected behavior

All components consuming that useQuery should re-render.

All useEffects ingesting the data should fire.

How often does this bug happen?

None

Screenshots or Videos

No response

Platform

  • React Native
  • Expo
  • React Query v4

Tanstack Query adapter

None

TanStack Query version

v4.36.1

TypeScript version

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions