redux-toolkit icon indicating copy to clipboard operation
redux-toolkit copied to clipboard

RTK Query: Conditionally skip the query from the endpoints definition for hooks

Open SYoder1 opened this issue 10 months ago • 17 comments

Is there a way to set a condition or even a callback on a endpoint to conditionally skip it, instead of at the hook using skip or skipToken?

I have a few queries in our app that we should only make if the user has a specific permission. These queries are all around our app, and currently we are having to add logic to ever screen in order to set the hook to skip the query. This is some unnecessary boilerplate code for most queries, and we have thought about making a wrapper hook to extend the skip logic to include the permissions. Is there a better way?

SYoder1 avatar Jan 10 '25 00:01 SYoder1

No, there is currently no way to skip a query from within the endpoint definitions.

It's a thought that's come up a couple times, but it's never been something we've seriously tried to figure out.

Wrapping the hooks with a custom hook is probably the right answer here.

markerikson avatar Jan 10 '25 00:01 markerikson

Ahh bummer.

I would be open to looking into it and creating a PR if that something you would consider and or would want.

I spent some time looking into the hook builder, and though about being able to pass a selector in the endpoint definition that returns a boolean. If it's true, skip the call like how it checks for the skipToken. https://github.com/reduxjs/redux-toolkit/blob/master/packages/toolkit/src/query/react/buildHooks.ts#L1085

SYoder1 avatar Jan 10 '25 03:01 SYoder1

Conceptually, this would probably need to be integrated into the existing condition method, which is already what we use to bail out of executing the thunks in a number of cases:

https://github.com/reduxjs/redux-toolkit/blob/aaeda1538993dbdbd9264d784f6b1bf78ebc94b0/packages/toolkit/src/query/core/buildThunks.ts#L526-L573

Honestly, it might even only be a 3-4 line change. Don't destructure getState, then add:

if (endpointDefinition.condition && endpointDefinition.condition(arg, options) === false) {
  return false
}

markerikson avatar Jan 10 '25 03:01 markerikson

I can play around with that and see how it works.

Would that still keep the hook in a isUninitialized state?

SYoder1 avatar Jan 10 '25 03:01 SYoder1

It should, yeah, because no actions get dispatched at all.

markerikson avatar Jan 10 '25 04:01 markerikson

Keep in mind that this will mess up the TS types a lot, which is the main reason we haven't done this yet.

phryneas avatar Jan 10 '25 06:01 phryneas

I spent some time on this today, and maybe I wasn't setting this up correctly, but as soon as the hook renders it is in a loading state. I tested this in buildThunks by calling the new function, as well as just adding a check for the endpoint name and returning false at the top of the function. Both ways still keep it "loading"

console.log(isUninitialized, isLoading, isFetching, isSuccess, isError)
// false true true false false

If I add skip: true to the hook, none of the buildThunks code runs (expected), and isUninitialized is still true

console.log(isUninitialized, isLoading, isFetching, isSuccess, isError)
// true false false false false

SYoder1 avatar Jan 10 '25 19:01 SYoder1

Ah, yeah, we do intentionally have an override selector that says "if it's uninitialized return loading: true on the grounds that the request will kick off right after this". So the actual cache entry isn't changing, but we overwrite the derived boolean flags on the way out.

markerikson avatar Jan 10 '25 20:01 markerikson

Would it be expected to keep it in a loading state for this use case?

SYoder1 avatar Jan 10 '25 20:01 SYoder1

Well, this is where the increasing number of options and behavioral interactions causes increasing complexity :)

The selector code has been able to make this optimization based on a valid assumption that "this thunk will be dispatched in an effect right after this, therefore we can safely assume that the cache entry will go to a status of pending, and we can save a re-render by preemptively overwriting the derived flags with isLoading: true".

If we now have a condition for the thunk, that assumption no longer holds true - it's possible that the effect will run but the thunk will bail out.

So, what's probably needed is to extend the logic in the pre-selector to be along the lines of if (entry.isUninitialized && !endpointDefinition.condition) {.

In other words, it should keep the existing isLoading override for endpoints by default, but if the endpoint does define condition, then we have to skip this flags optimization.

Also, I just glanced at the code. noPendingQueryStateSelector is currently defined at the top level of buildHooks.ts, but in order to have access to the current endpoint it would need moved inside of buildHooks() - see queryStatePreSelector as the similar example.

I was going to suggest moving it right after queryStatePreSelector, but the way we've got the logic inside of buildHooks defined is tricky - there's a bunch of function declarations that get hoisted, but noPendingQueryStateSelector is a const, so it doesn't hoist. Also, there's no good way to give it access to the current endpoint name without some further refactoring.

It's feasible, but it'll take some shuffling of the code.

markerikson avatar Jan 10 '25 20:01 markerikson

@markerikson maybe I still don't fully understand the nuances of it but I just added the check to the same condition for the skip and skipToken. It's still a draft MR, for the idea and still needs some work around documentation.

SYoder1 avatar Jan 13 '25 20:01 SYoder1

Hey @markerikson, if you have a second, could you take a look at the MR and let me know what you think?

SYoder1 avatar Jan 21 '25 16:01 SYoder1

@SYoder1 I won't have the mental space available to look at that until after I get the infinite query feature shipped. (Fortunately, that's getting fairly close!)

markerikson avatar Jan 21 '25 16:01 markerikson

Hey @markerikson, congrats on the launch of infinite query. We are starting to migrate the app I work on from our home built infinite query to this one.

If you have any free time, I would love a high level review on the MR to skip at the endpoint definition.

SYoder1 avatar Feb 26 '25 15:02 SYoder1

Hey @markerikson circling back around to this, I updated the PR after the latest update.

SYoder1 avatar Apr 25 '25 14:04 SYoder1

@markerikson can we get a review on this? My team could also use this functionality

jesse-one-app avatar May 05 '25 23:05 jesse-one-app

Sorry, it's on my list of stuff to look at, but I haven't had much time to work on RTK lately. Will try to get to it in the near future! (but can't promise a specific time)

markerikson avatar May 06 '25 00:05 markerikson