NOTE: I'm not trying to call the query hook in another component which should return cached data if the same arguments were passed to the hook avoiding a new request to be made, my issue is when forcing a refetch.
I'm working on a React Native project and I'm having a small confusion regarding the inner workings of RTK Query when it comes to refetching fresh data and I can't seem to find an answer online.
I have a query that's getting triggered when the component mounts, and I have a useEffect
that has the fetched data (which is an array of objects) in its dependencies array like so:
function MyComponent(props){
...
const {
data: teams,
refetch,
} = useGetUserTeamsQuery({userId}, {skip: !userId || !rolesToken});
useEffect(()=>{
if(teams){
console.log("teams changed...")
}
}, [teams])
}
Now when the component first mounts I get teams changed...
in the logs as expected, then when some event fires (socket reconnects) I call refetch
to refetch fresh data in case the list of teams changed when the socket was disconnected, and the behavior I'm getting is:
After the refetch, my useEffect
doesn't execute when I have the exact same result being returned from the server, which is confusing to me since I can see in my debugger that the request actually fires and goes from pending
status to fulfilled
, so normally I should be having a new reference to the array teams
thus my useEffect
should get triggered since the reference has changed even if the data is still the same.
Then when update the list of teams (adding the user to a new team for example) my useEffect
executes and I get the log in the console.
So my question is, does RTK Query perform some sort of comparison between the cached data and the newly fetched one to determine if the data has somehow changed and if the answer is no it would return the same reference ?
Yes, if a new query will return the same data as the last query, you will get the same object back, to prevent unexpected (and unneccessary) rerender.
Even if a query will return partially equal data, that part of data will be kept stable.
Your useEffect
here is a data synchronization mechanism - so unless data doesn't change, it will not reexecute. If this causes a problem with your logic, you are likely abusing useEffect
for something it should not be used for, and should probably rethink the actual logic that you are applying.
There is a way of disabling this feature called structural sharing - but I stand by my statement that you should never rely on a useEffect
like this.
The option can be added to your endpoint definition and is called structuralSharing
, here is the docblock:
/**
* Defaults to `true`.
*
* Most apps should leave this setting on. The only time it can be a performance issue
* is if an API returns extremely large amounts of data (e.g. 10,000 rows per request) and
* you're unable to paginate it.
*
* For details of how this works, please see the below. When it is set to `false`,
* every request will cause subscribed components to rerender, even when the data has not changed.
*
* @see https://redux-toolkit.js.org/api/other-exports#copywithstructuralsharing
*/
structuralSharing?: boolean