Search code examples
reactjsreact-nativeredux-toolkitrtk-query

Does RTK Query compare the newly re-fetched data with the cached one to determine if there was a change?


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 ?


Solution

  • 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