Laurent
Laurent

Reputation: 127

RTK Query hooks - preventing polling to auto refetch when arg change

I'm trying to refresh a token in a Query hook, with the polling feature every 9 seconds:

"/App.tsx"
..
...
const [storedToken, setStoredToken] = useState(getStoredToken());

const { data, error, refetch } = useRefreshUserQuery(storedToken, {
   pollingInterval: 9000,
   // refetchOnMountOrArgChange: false // -> This has no effect
});
...
..

The problem is, it re-fetches instantly when the token is set with setStoredToken(token). The new token is passed as argument to the Query hook storedToken and refetch immediately (like an infinite loop).

That would be pretty neat to be able to do this. Is there any better way to refresh a token with polling?

Upvotes: 3

Views: 6166

Answers (2)

Eugene Tusmenko
Eugene Tusmenko

Reputation: 994

I believe that issue is nothing to solve on RTK-Q level - it's a pretty common and expected "limitation" of hooks and rendering lifecycle architecture. And I feel that RTK-Q polling just won't fit your requirements here, of course, that you are trying to achieve - it's not actually polling in common sense. At least - it's conditional polling, which needs some more logic)

So I would solve this just by debouncing and useEffect:

  const [storedToken, setStoredToken] = useState<string>(getStoredToken());
  const [tokenDebounced] = useDebounce(storedToken, 9000);
  const { data } = useRefreshUserQuery(tokenDebounced);

  useEffect(() => {
    if (data) {
      setStoredToken(data);
      // console.log(newToken);
    }
  }, [data]);

The useEffect content and data content may differ, but the overall idea should be clear.

useDebounce is from https://www.npmjs.com/package/use-debounce, but your own implementations should work the same if you have some defined already.

Another idea, touching you AUTH setup a bit - is just avoid

const [storedToken, setStoredToken] = useState<string>(getStoredToken());

the part at all, and keep useRefreshUserQuery() without params.

Most likely and common is to store the token in localStorage or redux\other store, and define new baseQuery, based on fetchBaseQuery that will set header and\or to include cookies with credentials: "include" with a token from localStorage or redux\other store. Definitely, you will need to store it during the first AUTH then.

I think RTK-Q auth example reveals this case in some way also:

https://redux-toolkit.js.org/rtk-query/usage/examples#authentication

After you'll avoid that useState and query hook param - you'll be able to use polling with no issues:

const { data, error, refetch } = useRefreshUserQuery(undefined ,{
   pollingInterval: 9000,
});

Upvotes: 2

phry
phry

Reputation: 44196

"Polling" here means "fetch X seconds after I have data", but of course you have to get the first data itself - and that is that first fetch. If you prevent that, polling will also never start.

Tbh., this is kind of a weird requirement and doing it like this will fill your cache with dozens of state entries.

I'd do something a little differently - solve it in the endpoint lifecycle.

This is untested pseudocode and you'll need to adjust it a bit:

function waitFor(ms) {
  return new Promise(resolve => setTimeout(() => resolve("waited"), ms))
}


currentToken: build.query({
  query() {
    // whatever you need for the first token here
  },
async onCacheEntryAdded(
        arg,
        { updateCachedData, cacheDataLoaded, cacheEntryRemoved }
      ) {
        try {
          // wait for the initial query to resolve before proceeding
          await cacheDataLoaded

          while (true) {
            const result = await Promise.race(waitFor(9000), cacheEntryRemoved)
            if (result !== "waited") {
              // cache entry was removed, stop the loop
              break
            }
            // make a fetch call to get a new token here
            const newToken = fetch(...)

            updateCachedData((oldData) => newToken)
          }
      },
})

and then just

const result = useCurrentTokenQuery()

in your component

Upvotes: 1

Related Questions