Code Monkey home page Code Monkey logo

vue-query's Introduction

Vue Query logo

npm version npm license bundle size npm

build status codecov

⚠️ Package migration ⚠️

This package was migrated to be a part of https://github.com/TanStack/query.

If you are still on v1 make sure to follow migration guide first and then switch to @tanstack/vue-query.

If you are already on v2, just swap vue-query for @tanstack/vue-query in both of your package.json and import statements. Everything should still work as before.

Vue Query

Hooks for fetching, caching and updating asynchronous data in Vue.

Support for Vue 2.x via vue-demi

Based on react-query

Documentation

Visit https://vue-query.vercel.app

Visit https://vue-query-next.vercel.app for V2 documentation

For topics not covered in vue-query docs visit react-query docs as most of the concepts and APIs are the same.

Quick Features

  • Transport/protocol/backend agnostic data fetching (REST, GraphQL, promises, whatever!)
  • Auto Caching + Refetching (stale-while-revalidate, Window Refocus, Polling/Realtime)
  • Parallel + Dependent Queries
  • Mutations + Reactive Query Refetching
  • Multi-layer Cache + Automatic Garbage Collection
  • Paginated + Cursor-based Queries
  • Load-More + Infinite Scroll Queries w/ Scroll Recovery
  • Request Cancellation
  • (experimental) Suspense + Fetch-As-You-Render Query Prefetching
  • (experimental) SSR support
  • Dedicated Devtools
  • npm bundle size (depending on features imported)

Quick Start

  1. Install vue-query

    npm install vue-query
    # or
    yarn add vue-query

    If you are using Vue 2.x, make sure to also setup @vue/composition-api

  2. Initialize Vue Query via VueQueryPlugin

    import { createApp } from "vue";
    import { VueQueryPlugin } from "vue-query";
    
    import App from "./App.vue";
    
    createApp(App).use(VueQueryPlugin).mount("#app");
  3. Use query

    import { defineComponent } from "vue";
    import { useQuery } from "vue-query";
    
    export default defineComponent({
      name: "MyComponent",
      setup() {
        const query = useQuery("todos", getTodos);
    
        return {
          query,
        };
      },
    });
  4. If you need to update options on your query dynamically, make sure to pass them as reactive variables

    const id = ref(1);
    const enabled = ref(false);
    
    const query = useQuery(["todos", id], () => getTodos(id), { enabled });

vue-query's People

Contributors

aach avatar aantipov avatar damianosipiuk avatar dependabot[bot] avatar frandiox avatar henribru avatar kdnk avatar mosaab-emam avatar nexfader avatar ryanatvicesoftware avatar sondh0127 avatar tomasdurica avatar voxivoid avatar wobsoriano avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

vue-query's Issues

incomplete docs?

hello i really want to try this library, and i just reading the whole docs but i cannot find anything related to the mutation.
does mutation possible with this library?
or did i just miss the mutation section somewhere in the docs?

Compatibility with vue-i18n

Hi,

Thanks for all the effort you put into this project.
I've been having an issue using useQuery in vue components which also make use of the of the useI18n composable by vue3.

If both exist in the same component you end up with an error (in promise) TypeError: Cannot read property '__composer' of undefined at Proxy.mounted which is thrown by vue-i18n

Any ideas why this is happening?

setQueryData does not update the state

Hi!

I wanted to update cached data by using useQueryClient's setQueryData method. But the state returned from useQuery didn't get updated by updating the cache.

Could anyone show me a proper way to sync the state after updating the cache?

Reproduction link

codesandbox.io

Steps to reproduce

Clicking on Update button

What is expected?

data.title should get updated

What is actually happening?

The cache get updated. But the state does not

ref and reactive options

Hey,

I noticed that if you have values which are already a ref the options parameter doesn't recognise a change on those values. Is there a proper way to get around this?

Example

// enabled is externally changed
const enabled = ref(false);

const options = reactive({ enabled: enabled.value });

// if enabled is changed to true, the query does not fire
const q = useQuery('query', myFunction, options)

useMutation is returning plain values, NOT reactive reference ?

Hi. Thank you for porting react-query into Vue. I really like this pattern.

This is the return value of useMutation .

export interface UseMutationResult<TData = unknown, TError = unknown, TVariables = unknown, TContext = unknown> {
    context: TContext | undefined;
    data: TData | undefined;
    error: TError | null;
    failureCount: number;
    isError: boolean;
    isIdle: boolean;
    isLoading: boolean;
    isPaused: boolean;
    isSuccess: boolean;
    mutate: UseMutateFunction<TData, TError, TVariables, TContext>;
    mutateAsync: UseMutateAsyncFunction<TData, TError, TVariables, TContext>;
    reset: () => void;
    status: MutationStatus;
    variables: TVariables | undefined;
}

The returned values are all plain values. Shouldn't the values be ref instead, just like in useQuery?

I am trying to watch the value of isSuccess but I am not able to, due to it NOT being a reactive value.

How may I solve this?

v2 roadmap

react-query/core

List of tickets in react-query V4 (not finalized): https://github.com/tannerlinsley/react-query/issues?q=is%3Aissue+label%3Av4+is%3Aclosed

vue-query

  • hooks should wrap primitives as refs, but return functions as-is
    #123
  • create wrappers for QueryClient, QueryCache, MutationCache etc, so parameters could be passed as refs and would be automatically unwrapped
    #110
  • migrate devtools as a plugin to official vue devtools #155
  • nuxt 3 integration
  • expose persistance plugins (This will be handled after migration to Tanstack)

misc

  • Create migration to v2 page and notify about all noticeable changes in react-query/core and vue-query
  • Consider codemods?

Matching of reactive keys not working

I will give an example to show my issue. I have a query key like this: reactive(['products', 'show', productId]), where productId is a Ref. If I use setQueryData with this key, the change is not reflected in a query that uses exactly the same key. I know the problem is the ref because if I use this key instead it works: reactive(['products', 'show', productId.value]).

Default Options seems to not work

Vue 3 + Vite

Main.ts example

import {VueQueryPlugin, VueQueryPluginOptions} from "vue-query";

const vueQueryPluginOptions: VueQueryPluginOptions = {
  queryClientConfig: {
    defaultOptions: {
      queries: {
        refetchOnWindowFocus: false,
        refetchOnMount: false,
        retry: false
      },
    },
  },
}

const setupApp = () => {
  const app = createApp(App).use(VueQueryPlugin, vueQueryPluginOptions)
};

Use query composable:

import axios from 'axios'
import {GetAccountDetailsResponse, GetAccountResponse, GetSubscriptionResponse} from "../models/AccountModels"
import { useQuery } from "vue-query"


export default function() {
  // These requests rely on the id in the auth token
  const getAccount = (): Promise<GetAccountResponse> => axios.get('/account')

  const getAccountDetails = (): Promise<GetAccountDetailsResponse> => axios.get('/account/details')

  return {
    getAccount: useQuery<GetAccountResponse, Error>('account', getAccount),
    getAccountDetails: useQuery<GetAccountDetailsResponse, Error>('accountDetails', getAccountDetails),
  }
}

Script in a component:

<script setup lang="ts">
import useSubscriptionManagerApi from "../composables/useSubscriptionManagerApi";
import useScreenModes from "../composables/useScreenModes";
import Content from '../components/default/Content.vue'

const { mobileMode } = useScreenModes()
const { getAccount } = useSubscriptionManagerApi()

const { isLoading, isError, isFetching, data, error, refetch } = getAccount

</script>

The getAccount query is called multiple times, and is still called on every window focus.

If I think set the options per query it works as intended.

Allow custom clients

Currently the library does the great job but is forced to use a single query client, while a client isn't granular - there are no modules like in Vuex.

In my example I needed several clients with independent cache to be available in the same hierarchy of components. Currently this requires to fully rewrite useBaseQuery and other hooks that depend on it. Considering that utils.ts is unavailable from the outside, this needs to fork the library instead of wrapping it in custom hooks.

The suggestion is to allow multiple clients per application, this will make the library way more flexible. The only adjustment that is needed it is to inject a client through options instead of mandatory provide/inject:

function useBaseQuery(options, observer) {
  const queryClient = options.queryClient ?? useQueryClient();
  ...
function useMyQuery(options, observer) {
  return useQuery({...options, queryClient: myQueryClient}, observer);

This makes the library a lot more flexible. In my case myQueryClient couldn't be injected, but in custom hook this could be left to the discretion of a developer.

Could you add QueryObserver export?

So, I am in a situation where I need to use query result outside a composable function or setup. According to the QueryObserver api, the way to achieve that would be -

import { QueryObserver } from 'vue-query';
import { queryClient } from 'myQueryClient';

const classesObserver = new QueryObserver(queryClient, { queryKey: ['classes', dependencies] });
const classesData = classesObserver.getCurrentResult().data;

Now, difference between using query observer's getCurrentResult vs using query client's getQueryData/getQueryState would be
query observer will return the actual data which is cached along with its transformation done using select option. Whereas, queryClient's getQueryData/getQueryState will return just the response data.

So, could you possibly add QueryObserver to the vue-query exports?

reactive params not firing when staletime is set

I have the initialdata and stale time set. but QueryFunction does not get the callback when reactive params change.

const { data } = useQuery(
  reactive(["ITRQuery", { countPerPage }]),
  () =>
    getITRResult({
      ...props.condition,
      countPerPage: countPerPage.value,
    }),
  reactive({
    initialData: props.dto,
    refetchOnWindowFocus: false,
    refetchOnReconnect: false,
    retry: false,
    staleTime: twentyFourHoursInMs,
    useErrorBoundary: true,
  })
);

i had to either comment out initialData or staleTime which i don't want to do.

Objects with `value` key breaks vue-query dev tools on nuxt 2

Reproduction:

In chrome, the error is:

TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'Object'
    |     property '__ob__' -> object with constructor 'Observer'
    --- property 'value' closes the circle

In firefox:

TypeError: cyclic object value

The only change I made was the third line:

  await fetch("https://jsonplaceholder.typicode.com/todos")
    .then((response) => response.json())
    // The only change
    .then((it) => ({ value: it }))

I noticed this issue because a response had a value key.

As a side note, this sandbox also shows the error from #101.

Handing fetched data as a prop

Hi, first thanks for making this - I'm new to Vue and really missed React Query and how simple it made data fetching in React. Vue Query so far has been amazing.

I have a question rather than an issue: in my app I fetch data with useQuery (all good) and then pass that data as a prop to a child component, the child component however never updates. It seems something has been lost and is no longer reactive. Do I need to always add computed properties everywhere to tie this together? I've tried using keys on the component to force an update, but that also went nowhere.

I made a small repo demonstrating what I am trying to do in a bigger app:
https://codesandbox.io/s/vigilant-leaf-nzk5w

Any help would be appreciated as I've been hung up on this for weeks trying to figure it out 🙏🏼

UseQueryOptions interface is not exposed

Hello!
While using this awesome library, I noticed that some useful interfaces like UseQueryOptions or UseMutationOptions are not exposed.

I was creating wrapper functions for my queries/mutations, and wanted to add an options parameter to override query options:

function useMyQuery (options?: UseQueryOptions) {
  return useQuery(
    'my-query', 
    async () => fetch('/api'),
    {
      retry: false,
      ...options
    }
  );
}

Unfortunately this is not possible, because the interface UseQueryOptions is not exposed by vue-query.
I've tried importing it directly from react-query, but that caused some issues ("Cannot find module 'react' or its corresponding type declarations.").

I've also tried to add the following line to /src/index.ts:

export { UseQueryOptions, UseMutationOptions } from "react-query/types/react/types";

This worked without any issues and I believe that would be a decent solution.
If you agree, I'd be happy to create a PR 🙂
Maybe it would be also good to expose all other interfaces and types in react-query/types/react/types as well.

Error when used inside <script setup>

So I have this code:

<script lang="ts" setup>
  import { useProfileListQuery } from "@/hooks/useProfiles"

  const { isLoading, data } = useProfileListQuery()

  /* ... */
</script>

And i get the error:

vue-query hooks can only be used inside setup() function

But, I am in a sort-of setup function, aren't I?

How can we use a computed property in the query key (e.g. page number)?

The react-query docs describe how to use a compound query key (Array Keys). I want to be able to do something similar with vue-query, but I've struggled to make it work.

💡 Proposed Vue 3 Composition API Example:

export default defineComponent({
  // ...
  setup: (props) => {
    const queryKey = `widgets:${props.query}`;
    const page = ref(0);
    const { data: widgets, isFetching: fetching } = useQuery<Widget[]>(
      reactive([queryKey, computed(() => page.value)],
      () =>
        getWidgets({
          p: page.value,
          q: props.query,
        })
    )
})

... but that doesn't work as-is. Changing the page value does not trigger a re-query. The computed does't appear to do anything; and otherwise I need to explicitly trigger an invalidation (e.g. queryClient.invalidateQueries([queryKey, 0]);).

Is there some other method I'm missing for how to use a reactive value in the QueryKey?

useQueries still executes if each query's enabled is set to false

Hi there, thanks for your hard work on this library! It's proved very useful to me.

I'm trying out useQueries for the first time today and it seems that it acts in a way that I did not anticipate. Even if the enabled property for the query is set to false, then the API is still called.

Please check out my sandbox here: https://codesandbox.io/s/musing-scott-91n4r?file=/src/App.vue

Is this expected? Or should I be disabling this in another way?

Disable refetch?

How can I disable refetch?

Here is my QueryClient:

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      refetchOnReconnect: false,
      refetchIntervalInBackground: false,
      refetchOnMount: false,
      refetchOnWindowFocus: false,
    },
  },
});
export default {
 /* ...rest */
  setup() {
    useQueryProvider(queryClient);
  },
};

But when I switch to another tab, then go back to website's tab. It refetch itself. Is this a bug?

Error loading devtools in Vue 2

When trying to load devtools, I get this error:
export resolveComponent (imported as 'VueDemi') was not found in 'vue-demi'
Versions:
vue: 2.6.12,
vue-demi: 0.12.1,
vue-query: 1.15.0

Usage with jest

Hi!

I wanted to test a shallow mounted component with jest and vue-test-utils@next and as you could imagine I run into

No queryClient found in Vue context, use 'useQueryProvider' to set one in the root component.

Any ideas how to approch this issue besides writing a jest mock?

Reactive options is behaving weird?

Hello, I have a simple example where I want to disable the query when an input is undefined or an empty string. Here's my code:

// api.js
import { reactive } from "vue";
import { useQuery } from "vue-query";

const fetcher = async (id) => {
  const res = await fetch("https://jsonplaceholder.typicode.com/todos/" + id);
  const data = await res.json();
  return data;
};

export function useGetTodo(id) {
  const key = reactive(["todo", id]);
  const options = reactive({
    enabled: !!id
  });

  return useQuery(key, () => fetcher(id.value), options);
}
// Component.vue
<template>
  <input type="text" v-model="id" />
  <div v-if="isLoading">Loading...</div>
  <div v-else>{{ JSON.stringify(data) }}</div>
</template>

<script setup>
import { ref } from "vue";
import { useGetTodo } from "../api";

const id = ref("");

const { data, isLoading } = useGetTodo(id);
</script>

First thing I expect here is that on first load, since the input is empty, the query will not run. The options is inside the reactive property and to my surprise, if I am missing something, it is still running.

You can check it here https://codesandbox.io/s/vue-query-reactive-options-forked-nslhg

QueryClient setQueryData updater old data is undefined

Trying to use QueryClient's setQueryData method to update the cached data after a mutation with an updater function, the oldData param is undefined. Am I doing something wrong?

import { useQuery, QueryClient } from 'vue-query'
import { folderResource } from '../apiroids/resources/folder.resource'
import { FolderDto } from '@shared/folder/dto/folder.dto'

const queryClient = new QueryClient()

const pageSize = ref(5)
const currentPage = ref(1)

const paginationParams = computed(() => ({
  skip: pageSize.value * (currentPage.value - 1),
  take: pageSize.value,
}))

const queryKey = reactive(['folders', { paginationParams }])

const { isLoading, data } = useQuery(
  queryKey,
  () => folderResource.getAll({
    queryParams: paginationParams.value,
  }),
  {
    keepPreviousData: true,
  },
)

const onFolderCreated = (newFolder: FolderDto) => {
  queryClient.setQueryData(queryKey, (oldData: any) => {
    console.log(oldData) // undefined
    return [...oldData, newFolder]
  })
}

A folder is created in a modal component:

const { mutate: createFolder } = useMutation(
  () => folderResource.create(folder),
  {
    onSuccess: (data) => {
      emit('created', data.data)
      emit('update:show', false)
    },
  },
)

how to use vue-query with vue2 in a html page?

with vue2, I npm install vue-query @vue/composition-api
then

import { VueQueryPlugin } from "vue-query";
import { VueQueryDevTools } from "vue-query/devtools";
Vue.use(VueQueryPlugin);

But I do not know how to do next to use vue-query in a html page. Is there any example to use vue-query in this app?

var appBoard = new Vue({
      el: '#board',
      data:function () {
        return {
          layout: my_fetch('xxx'),
        }
      }
  })

Error with Vue 2 - No queryClient found in Vue context

Hello,

Tried this out with my Vue 2 codebase.

This is how I am mounting app

import { QueryClient, VUE_QUERY_CLIENT } from "vue-react-query";

import VueCompositionApi from "@vue/composition-api";
import { createApp, h } from "@vue/composition-api";

Vue.use(VueCompositionApi);
const queryClient = new QueryClient();
queryClient.mount();
createApp({
  provide: {
    [VUE_QUERY_CLIENT]: queryClient
  },
  router,
  store,
  render: () => h(App)
}).mount("#app");

And this is how I am using it in my component

export default {
  setup() {
    const { isLoading, isError, isFetching, data, error, refetch } = useQuery(
      "todos",
      todoFetcher,
      {
        retry: 0,
        staleTime: 1000,
        cacheTime: 2000
      }
    );
    return { isLoading, isError, isFetching, data, error, refetch };
  },
  name: "home"
};

Here are my dependencies

"dependencies": {
    "@vue/composition-api": "^1.0.0-rc.7",
    "core-js": "^3.4.4",
    "vue": "^2.6.11",
    "vue-class-component": "^7.0.2",
    "vue-property-decorator": "^8.3.0",
    "vue-react-query": "^0.4.0",
  },

And this is the error I am getting
image

Also, getting this warning in a different codebase.
image

Thank you!

Query keys containing computeds that evaluate to undefined aren't cloned properly

If a ref in the query key returns undefined, undefined will be returned here: https://github.com/DamianOsipiuk/vue-query/blob/main/src/vue/utils.ts#L151

But at https://github.com/DamianOsipiuk/vue-query/blob/main/src/vue/utils.ts#L128-L130, we only return the result if it wasn't undefined. undefined is interpreted as "okay, it wasn't a ref, let's keep going", but undefined could in fact have been the actual value of the computed. The end result of this is that you don't get the computed value in your query key, but the full computed object with all of Vue's internal attributes. For me this ultimately ends in a TypeError: cyclic object value when react-query tries to JSON.stringify the query key containing the computed.

Functions do not have to be Ref?

const { data, refetch } = useQuery("key", () => Promise.resolve(1), {
      refetchOnWindowFocus: false,
      enabled: false,
    });
refetch.value(); // manual exec

Types break when using reactive in options

I have this in a query:

return useInfiniteQuery<ProductQueryFn, APIError>(
  queryKey,
  queryFn,
  {
    getNextPageParam: ({ pagy: { next: nextPage } }) => ((nextPage === null) ? undefined : nextPage),
  },
);

But when I try to turn options into reactive, typescript stops recognizing the type of the params of getNextPageParam. This is the code where it doesn't work:

return useInfiniteQuery<ProductQueryFn, APIError>(
  queryKey,
  queryFn,
  reactive({
    enabled: computed(() => true),
    getNextPageParam: ({ pagy: { next: nextPage } }) => ((nextPage === null) ? undefined : nextPage),
  }),
);

Vite SSG and initial state

I've been working of progressively enhancing the rendering of one SSG webapp built with https://github.com/antfu/vite-ssg
So far vue-query looks better than trying to use Apollo and Pinia together.
I've been able to define a store like this :

export const useAuditQueryStore = () => {
    const fetcher = async (): Promise<RootQuery> =>  graphQLClient.request(query)
    const { data, isLoading, suspense } = useQuery("allAudit", fetcher, { staleTime: 1000 })
    const audits = computed(() => data?.value?.allAudit ?? [])
    onServerPrefetch(suspense)
    return {
        suspense,
        auditsLoading: isLoading,
        audits
    }
}

The template looks a little like this :

<template>
    <div class="Wrapper">
        <div class="Title">Projects</div>
        <div class="ProjectList">
            <div v-if="isLoading">Loading ...</div>
            <HelloWorld v-if="data?.myData" />
        </div>
    </div>
</template>

and then to import that in the script setup of my components, and it does work as expected, the build output contains the rendered layout. However the initialState is empty.
The page initially load, then the markup is removed and the loading appears, then the markup re-appears.

I'm not sure if I'm doing anything wrong at this stage, and before further investigations I wanted to know if vue-query would work with vite-ssg.

Thanks!

Suspense not working in SSR

Hi! Looks like suspense is always undefined during SSR?
This is the official example: https://txnjc.sse.codesandbox.io/ , but its rendered HTML doesn't contain the todos (check the view-source tool).

After setting up Suspense in App.vue, I'm trying the following in a component:

   async setup() {
    const { data, suspense } = useQuery('todos', fetcher)

    const p = suspense()
    console.log(p)
    await p

    return { data }
  }

It prints undefined in the server and a promise in the browser.

My end goal is just getting a promise to await in the server for SSR:

  setup() {
    const { data, suspense } = useQuery('todos', fetcher)

    onServerPrefetch(() => suspense())

    return { data }
  }

Any idea how to do this without too much boilerplate? (simplified version of useQueryClient + prefetchQuery)

useQueries not working when array of query options is computed (dynamic number of queries)

Hello, its me again

So, here's something I was trying with useQueries. I am trying make useQueries work with dynamically created query options. This will let me call todos for multiple ids which are dynamically passed to the hook.

function useMultipleTodos() {
  const ids = ref([]);
  const queries = computed(() => ids.value.map((id) => ({
    queryKey: ['post', reactive({ id })],
    queryFn: todoFetcher(id),
    enabled: !!id,
  })));
  const queryResults = useQueries(queries.value);
  return reactive({
    queryResults,
    ids,
    queries,
    fetch: (_ids) => { ids.value = _ids; }
  });
}

and in the component I would use it something like this

export default defineComponent({
  name: "App",
  components: { VueQueryDevTools },
  setup() {
    useQueryProvider();
    return {
      multipleTodos: useMultipleTodos(),
    };
  },
  methods: {
    onFetchMultipleTodos() {
      this.multipleTodos.fetch([1,2,3]);
    }
  }
});

Now, when my onFetchMultipleTodos method is called, it calls fetch method and fetch method sets ids ref to [1,2,3]. This in turn makes a change in queries computed. But, still queries are not called, nothing in the vuequery dev console.

Here's the link to the repo which reproduces this in our basic-vue-2 example.
https://github.com/onkarj422/reproduce_use_queries

How to set initial data?

In react-query, I can use const {data = []} = useQuery(...), since empty data is undefined, I want to know how to set set in vue-query?

Re-export extra types from react-query

Hello!

I'm wrapping the useQuery in custom hooks to reuse them accross the application, for example:

export const useFetchTodos = (options) => {
  return useQuery('todos', todosFetcher, options)
}

The problem I'm facing is that I'd need UseQueryOptions to be able to type it, since otherise it's any. I was thinking about opening a PR adding that type to the index.ts :

export type {
  UseQueryOptions
} from "react-query/types";

What do you think?

Suggested way of accessing queries from Vue Router navigation guards? (Vue 2/VueRouter 3)

When trying to access a query within the router.beforeEach method, we get the following error: Error: vue-query hooks can only be used inside setup() function..

Is there a suggested way to be able to access queries when using Vue Router?

The main option I've come up with is syncing some data to the Vuex store via a query's onSuccess hook. Not ideal, but it could technically work.

Vue 2.x without composition api?

So I just look at the example, I can see it's using composition API, I'm new to Vue, and I don't have time to rewrite my codebase to composition api. (maybe I spell wrong). Is there any way to use this library? I'm in love with react-query so I'd love to use another version of it. Thank you :)

More generic SSR support

Hi, thanks for this library! I'm maintaining a couple of packages for SSR with Vite and I'm interested in supporting vue-query. I see the SSR support is experimental so I would like to add some feat requests to make it easier to integrate with other tools.

It would be great if we could create and provide a Query Client outside of a component, just like any other Vue library that is installed when creating the app:

const queryClient = new QueryClient({ ... })

app.use(queryClient) // This would use queryClient.install method to run `app.provide(..., queryClient)`

Everything else could probably work in the same way, but this gives us the ability to export this created client to pass it to any SSR tool we need (for callinghydrate or dehydrate), which are normally outside of component scopes.
Thanks!

TypeScript - React's JSX global namespace is being used over Vue's

Hi, thanks for exposing this great library to the Vue community.

I am using .tsx files for my Vue project as I prefer the TypeScript safety. All was well until I installed vue-query. Then all my JSX broke and the error messages seem to show that the interfaces of my vue components were not matching interfaces that appeared to be inline with React's JSX definition.

I dug in a bit and my suspicions were confirmed. It appears that you are importing the React types, which inadvertently triggers the namespace collision.

It's the first time I have experienced this with TypeScript so I have no recommendation unfortunately. I am hoping I can somehow give Vue's definitions priority.

"No queryClient" error when using useQuery in async watch

I'm getting the error below

Error: No queryClient found in Vue context, use 'useQueryProvider' to set one in the root component.

with the component showed below.

Steps to Reproduce
https://www.loom.com/share/ab4cfe92a11c4d88b8048fa2ef8cd1ea

Working Sample Repo
https://github.com/vicesoftware/vue-3-vue-query-boilerplate

Component

<script>
import { defineComponent, reactive, toRefs, watch, computed } from "vue";
import { useQuery, useQueryClient } from "vue-query";
import { useRouter } from "vue-router";
import { getPosts } from "./posts.data";
import { getUsers } from "../users";

export default defineComponent({
  name: "PostsList",
  setup() {
    const router = useRouter();

    const data = reactive({
      router: {},
      dataRequested: false,
      postsQuery: {},
      usersQuery: {},
      isLoading: computed(
        () => !data.dataRequested || (data.postsQuery.isLoading || data.usersQuery.isLoading)
      ),
      isError: computed(
        () => data.postsQuery.isError || data.usersQuery.isError
      ),
      error: computed(
         () => data.isError ? (data?.postsQuery.error || data?.usersQuery.error) : ""
      ),
      hasData: computed( 
        () => !!data.postsQuery.data && !!data.usersQuery.data
      ),
      posts: computed(
        () => data.postsQuery && data.postsQuery.data
      ),
      users: computed( 
        () => data.usersQuery && data.usersQuery.data
      ), 
      selectedUser: computed(
        () => router?.currentRoute?.value?.query?.username
      )
    });

    const queryClient = useQueryClient();

    const loadData = async () => {
      const postsCacheKey = ['posts'];
      let selectedUser;

      if (data.selectedUser) {        
        const users = queryClient.getQueryData("users");

        if (!users) { // we have a selected users but no users in cache
          // do a dependent query
          return;
        }

        console.log(users);
        selectedUser = users.find(user => user.username === data.selectedUser);
        console.log(selectedUser);
        postsCacheKey.push(`userid=${selectedUser.id}`);
      } 

      const postsQuery = useQuery(
        postsCacheKey,
        () => getPosts({userid: selectedUser?.id})
      );

      const usersQuery = useQuery(
        "users",
        () => getUsers()
      );

      const [postsQueryResponse, usersQueryResponse] =  await Promise.all([
        postsQuery, usersQuery]);

      data.dataRequested = true;

      data.postsQuery = postsQueryResponse;
      data.usersQuery = usersQueryResponse;
    }

    const changeItem = ($event) => {
      router.push({ name: 'Posts', query: { username: $event.target.value }});
    }

    watch(router.currentRoute, loadData, { immediate: true })

    return { ...toRefs(data), changeItem };
  }
});
</script>

<template>
  <h1>Posts</h1>
  <p>
    As you visit the posts below, you will notice them in a loading state the
    first time you load them. However, after you return to this list and click
    on any posts you have already visited again, you will see them load
    instantly and background refresh right before your eyes!
    <strong>
      (You may need to throttle your network speed to simulate longer loading
      sequences)
    </strong>
  </p>
  <div v-if="isLoading">Loading...</div>
  <div v-else-if="isError">An error has occurred: {{ error }}</div>
  <div v-else-if="hasData">
    <select v-on:change="changeItem">
      <option disabled value="" selected>Select a user...</option>
      <option v-bind:key="user.id" v-for="user in users" :value="user.username" :label="user.name">
      </option>
    </select>   
    <ul>
      <li v-for="item in posts" :key="item.id">
        <router-link
            :to="{ name: 'Post', params: { id: item.id } }"
            >{{item.title}}</router-link
          >
      </li>
    </ul>
  </div>
</template>

<style scoped>
.visited {
  font-weight: bold;
  color: green;
}
</style>

Creating a query which uses parameters from the consuming component

Let's suppose I have a composable function which is using getCurrentInstance to get some property from the component instance -

function useObjectsByClassId() {
    const { studyId, latestChangesetId } = toRefs(useInputDataParams());
    const instance: any = getCurrentInstance();
    const modelClassId = computed(() => get(instance, 'proxy.modelClass.classId'));
    console.log(modelClassId);
    return {
        nothing: '',
    };
}

This logs a ref to the modelClassId, which gets updated throughout the component lifecycle.
Basically logs something like this
image

Now, if I want to use this modelClassId from my component to create my query like this -

function useObjectsByClassId() {
    const instance: any = getCurrentInstance();
    const modelClassId = computed(() => get(instance, 'proxy.modelClass.classId'));
    const queryKey = 'objectsByClassId';
    const queryParams = reactive({
        apiFn: 'getObjectsByClassId',
        params: [modelClassId],
    });
    const options = reactive({
        ...DEFAULT_OPTIONS,
        enabled: !!modelClassId,
    });
    return reactive({
        ...useQuery([queryKey, queryParams], queryFn, options),
    });
}

Now when i run this, i get modelClassId as undefined in the queryParams in my queryFn.
image

Been trying to figure this out from 2 days.
Please, let me know if you need any other info. Thanks!

Bottom line is, how do we create a query which needs some parameters from the component in which the useQuery is being consumed?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.