Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ngokevin opened this issue Feb 3, 2024 · 1 comment

Comments

@ngokevin
Copy link

ngokevin commented Feb 3, 2024

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

@TkDodo
Copy link
Collaborator

TkDodo commented Feb 3, 2024

The sandbox errors with:

Devbox not found
It's likely that the Devbox you're trying to access doesn't exist or you don't have the required permissions to access it.

I added a Code Sandbox but I was unable to reproduce the issue in that environment.

Then the issue is somewhere else in your code. Please try to find it in your code so that you can provide an actual failing reproduction

@TkDodo TkDodo closed this as not planned Won't fix, can't repro, duplicate, stale Feb 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants