Skip to content
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

feat(react-query): allow useQuery and useQueries to unsubscribe from the query cache with an option #8348

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

TkDodo
Copy link
Collaborator

@TkDodo TkDodo commented Nov 25, 2024

No description provided.

Copy link

nx-cloud bot commented Nov 25, 2024

☁️ Nx Cloud Report

CI is running/has finished running commands for commit 550b504. As they complete they will appear below. Click to see the status, the terminal output, and the build insights.

📂 See all runs for this CI Pipeline Execution


✅ Successfully ran 2 targets

Sent with 💌 from NxCloud.

@TkDodo
Copy link
Collaborator Author

TkDodo commented Nov 25, 2024

@hirbod could you help me re-vamp the React Native docs page if we ship this?

Copy link

codecov bot commented Nov 25, 2024

Codecov Report

Attention: Patch coverage is 87.50000% with 1 line in your changes missing coverage. Please review.

Project coverage is 84.21%. Comparing base (d6621a8) to head (550b504).

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##             main    #8348       +/-   ##
===========================================
+ Coverage   46.12%   84.21%   +38.08%     
===========================================
  Files         200       26      -174     
  Lines        7502      361     -7141     
  Branches     1715      102     -1613     
===========================================
- Hits         3460      304     -3156     
+ Misses       3667       48     -3619     
+ Partials      375        9      -366     
Components Coverage Δ
@tanstack/angular-query-devtools-experimental ∅ <ø> (∅)
@tanstack/angular-query-experimental ∅ <ø> (∅)
@tanstack/eslint-plugin-query ∅ <ø> (∅)
@tanstack/query-async-storage-persister ∅ <ø> (∅)
@tanstack/query-broadcast-client-experimental ∅ <ø> (∅)
@tanstack/query-codemods ∅ <ø> (∅)
@tanstack/query-core ∅ <ø> (∅)
@tanstack/query-devtools ∅ <ø> (∅)
@tanstack/query-persist-client-core ∅ <ø> (∅)
@tanstack/query-sync-storage-persister ∅ <ø> (∅)
@tanstack/react-query 95.91% <87.50%> (+0.37%) ⬆️
@tanstack/react-query-devtools 10.00% <ø> (ø)
@tanstack/react-query-next-experimental ∅ <ø> (∅)
@tanstack/react-query-persist-client 100.00% <ø> (ø)
@tanstack/solid-query ∅ <ø> (∅)
@tanstack/solid-query-devtools ∅ <ø> (∅)
@tanstack/solid-query-persist-client ∅ <ø> (∅)
@tanstack/svelte-query ∅ <ø> (∅)
@tanstack/svelte-query-devtools ∅ <ø> (∅)
@tanstack/svelte-query-persist-client ∅ <ø> (∅)
@tanstack/vue-query ∅ <ø> (∅)
@tanstack/vue-query-devtools ∅ <ø> (∅)

@hirbod
Copy link
Contributor

hirbod commented Nov 25, 2024

@TkDodo Sure. How should we proceed? Should we keep what we have, replace it, or additionally add the new solution?
While we're at it, we should also add a section for react query devtools in expo

I love that plugin

@TkDodo
Copy link
Collaborator Author

TkDodo commented Nov 25, 2024

Yeah there are some mentions about devtools on the top of the page, as well as on the devtools page: https://tanstack.com/query/v5/docs/framework/react/devtools

I think what I would want is:

Regarding the sections, I think we should do:

  • Devtools
  • Online status management
  • Refetch on app focus
  • The new thing :)

The new thing should cover what you can do with subscribe, maybe show the standard example with react-navigation, maybe show a more advanced example with the react-native stack (e.g. keep the top two screen subscribed)

I don’t think we need these anymore:

  • Refresh on Screen focus
  • Disable re-renders on our of focus Screens
  • Disable queries on out of focus screens

if we can cover everything with the subscribed flag

Copy link

pkg-pr-new bot commented Nov 25, 2024

Open in Stackblitz

More templates

@tanstack/angular-query-devtools-experimental

npm i https://pkg.pr.new/@tanstack/angular-query-devtools-experimental@8348

@tanstack/angular-query-experimental

npm i https://pkg.pr.new/@tanstack/angular-query-experimental@8348

@tanstack/query-async-storage-persister

npm i https://pkg.pr.new/@tanstack/query-async-storage-persister@8348

@tanstack/query-broadcast-client-experimental

npm i https://pkg.pr.new/@tanstack/query-broadcast-client-experimental@8348

@tanstack/eslint-plugin-query

npm i https://pkg.pr.new/@tanstack/eslint-plugin-query@8348

@tanstack/query-core

npm i https://pkg.pr.new/@tanstack/query-core@8348

@tanstack/query-devtools

npm i https://pkg.pr.new/@tanstack/query-devtools@8348

@tanstack/query-persist-client-core

npm i https://pkg.pr.new/@tanstack/query-persist-client-core@8348

@tanstack/query-sync-storage-persister

npm i https://pkg.pr.new/@tanstack/query-sync-storage-persister@8348

@tanstack/react-query

npm i https://pkg.pr.new/@tanstack/react-query@8348

@tanstack/react-query-devtools

npm i https://pkg.pr.new/@tanstack/react-query-devtools@8348

@tanstack/react-query-next-experimental

npm i https://pkg.pr.new/@tanstack/react-query-next-experimental@8348

@tanstack/react-query-persist-client

npm i https://pkg.pr.new/@tanstack/react-query-persist-client@8348

@tanstack/solid-query

npm i https://pkg.pr.new/@tanstack/solid-query@8348

@tanstack/solid-query-devtools

npm i https://pkg.pr.new/@tanstack/solid-query-devtools@8348

@tanstack/solid-query-persist-client

npm i https://pkg.pr.new/@tanstack/solid-query-persist-client@8348

@tanstack/svelte-query

npm i https://pkg.pr.new/@tanstack/svelte-query@8348

@tanstack/svelte-query-devtools

npm i https://pkg.pr.new/@tanstack/svelte-query-devtools@8348

@tanstack/svelte-query-persist-client

npm i https://pkg.pr.new/@tanstack/svelte-query-persist-client@8348

@tanstack/vue-query

npm i https://pkg.pr.new/@tanstack/vue-query@8348

@tanstack/vue-query-devtools

npm i https://pkg.pr.new/@tanstack/vue-query-devtools@8348

commit: 550b504

@hirbod
Copy link
Contributor

hirbod commented Nov 25, 2024

Yes, so far I can't think of a scenario that wouldn't work with the new subscribed prop.
One question remains though, what prop needs to be set to disable it:
refetchOnWindowFocus or refetchOnMount?

@TkDodo
Copy link
Collaborator Author

TkDodo commented Nov 25, 2024

There’s a new prop: subscribed: boolean. You can set that to false to detach the observer from the component.

@TkDodo
Copy link
Collaborator Author

TkDodo commented Nov 25, 2024

so:

const subscribed = useShouldBeSubscribed()

useQuery({
  queryKey,
  queryFn,
  subscribed
})

where useShouldBeSubscribed needs to figure out if it’s the top screen or not, in a reactive way (which is something that I was hoping you could fill in for me because I don’t know how that’s done)

@hirbod
Copy link
Contributor

hirbod commented Nov 25, 2024

Well, basically, subscribed should use the result of useIsFocused.
This hook is reactive and automatically updates when a screen is focused or blurred.

If we want to keep both the second and the topmost subscribed, we’d need to use a mix of useNavigationState and useIsFocused.

Untested, something like this:

const isTopScreen = useNavigationState((state) => {
    const currentRouteKey = route.key;
    const topRouteKey = state.routes[state.routes.length - 1].key;
    return currentRouteKey === topRouteKey;
});

const isSecondScreen = useNavigationState((state) => {
    if (state.routes.length > 1) {
        const secondRouteKey = state.routes[state.routes.length - 2].key;
        return route.key === secondRouteKey;
    }
    return false;
});

IMO, it's overkill. Since focus should ideally trigger a refetchOnWindowFocus, a disabled subscription would be re-enabled the moment useIsFocused changes again, which I believe is sufficient.

@flodlc
Copy link
Contributor

flodlc commented Nov 29, 2024

Great to see that the experience with focused screens in React Native is improving.
Just one thing: while the subscribe flag seems like a good solution, it will need to be set to true or false during navigation. This will trigger a re-render on navigation, which is not recommended for smooth, high-quality transitions.

Would an imperative function do the job instead?

Currently, I’m doing something like this, but I’m encountering issues with the isStale() function retuning false while it's actualy stale.

export const useEnabledOnFocus = (enabled = true): ((query: Query) => boolean) => {
  const isFocused = useRef(false);
  const query = useRef<Query<unknown, Error, unknown, QueryKey>>();
  useFocusEffect(
    React.useCallback(() => {
      isFocused.current = true;
      const task = InteractionManager.runAfterInteractions(() => {
        if (
          enabled &&
          ((query.current?.state.status === "pending" &&
            query.current?.state.fetchStatus === "idle") ||
            query.current?.isStale())
        ) {
          query.current?.fetch();
        }
      });

      return () => {
        isFocused.current = false;
        task.cancel();
      };
    }, [])
  );

  return (_query: Query<unknown, Error, unknown, QueryKey>) => {
    query.current = _query;
    const computedEnabled = enabled && isFocused.current;
    return computedEnabled;
  };
};

Usage:

useQuery({
    ...,
    enabled: useEnabledOnFocus()
})

@hirbod
Copy link
Contributor

hirbod commented Nov 29, 2024

FYI, runAfterInteractions does not fire in native-stack, only in the JS stack. And since the native-stack is the recommended way, re-renders are not a concern, as the navigation runs on the UI thread.

@flodlc
Copy link
Contributor

flodlc commented Nov 29, 2024

Ok, thanks for the explanation ! Can't wait to try the new api
Edit: Bottom Tab Navigator is not native

@TkDodo TkDodo marked this pull request as ready for review November 29, 2024 16:32
@TkDodo
Copy link
Collaborator Author

TkDodo commented Nov 29, 2024

@hirbod did you have some time to try out the preview build?

@hirbod
Copy link
Contributor

hirbod commented Nov 29, 2024

@TkDodo will do this weekend. The week was very tough with our release and hotfixes :D

@hirbod
Copy link
Contributor

hirbod commented Dec 13, 2024

@flodlc There’s one with nearly 100% feature parity and React Navigation adapters (also works with Expo Router): https://github.com/okwasniewski/react-native-bottom-tabs.

Either way, bottom tabs simply switch screens without animation, so there’s no such thing as jank with them.

@TkDodo finally have so time, pulling now and testing

@hirbod
Copy link
Contributor

hirbod commented Dec 13, 2024

@TkDodo Also, check Discord, but FYI: I gave it a thorough test—it works perfectly fine. LGTM!

@flodlc
Copy link
Contributor

flodlc commented Dec 13, 2024

Actually I was thinking about the react navigation implementation that allows "shift" animation.
Not sure if it really impacts the animation though

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants