Skip to content

Commit 741cc3f

Browse files
feat: vue-query adapter (#4254)
* feat: vue-query adapter * fix: umd build * chore: add basic example * docs: add vue adapter page * docs: add readme * fix: shields in readme, package ver, ts config * docs: fix one more link * fix: removed unused params RELEASE_ALL
1 parent 9f48dff commit 741cc3f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+4930
-26
lines changed

.codesandbox/ci.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"installCommand": "install:csb",
3-
"sandboxes": ["/examples/react/basic", "/examples/react/basic-typescript", "/examples/solid/basic-typescript"],
3+
"sandboxes": ["/examples/react/basic", "/examples/react/basic-typescript", "/examples/solid/basic-typescript", "/examples/vue/basic"],
44
"packages": ["packages/**"],
55
"node": "16"
66
}

docs/adapters/vue-query.md

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,52 @@
11
---
2-
title: Vue Query (Coming Soon)
2+
title: Vue Query
33
---
44

5-
> ⚠️ This module has not yet been developed. It requires an adapter similar to `react-query` to work. We estimate the amount of code to do this is low-to-moderate, but does require familiarity with the Vue framework. If you would like to contribute this adapter, please open a PR!
5+
The `vue-query` package offers a 1st-class API for using TanStack Query via Vue. However, all of the primitives you receive from these hooks are core APIs that are shared across all of the TanStack Adapters including the Query Client, query results, query subscriptions, etc.
66

7-
The `@tanstack/vue-query` package offers a 1st-class API for using TanStack Query via Vue. However, all of the primitives you receive from this API are core APIs that are shared across all of the TanStack Adapters including the Query Client, query results, query subscriptions, etc.
7+
## Example
8+
9+
This example very briefly illustrates the 3 core concepts of Vue Query:
10+
11+
- [Queries](guides/queries)
12+
- [Mutations](guides/mutations)
13+
- [Query Invalidation](guides/query-invalidation)
14+
15+
```vue
16+
<script setup>
17+
import { useQueryClient, useQuery, useMutation } from "vue-query";
18+
19+
// Access QueryClient instance
20+
const queryClient = useQueryClient();
21+
22+
// Query
23+
const { isLoading, isError, data, error } = useQuery(["todos"], getTodos);
24+
25+
// Mutation
26+
const mutation = useMutation(postTodo, {
27+
onSuccess: () => {
28+
// Invalidate and refetch
29+
queryClient.invalidateQueries(["todos"]);
30+
},
31+
});
32+
33+
function onButtonClick() {
34+
mutation.mutate({
35+
id: Date.now(),
36+
title: "Do Laundry",
37+
});
38+
}
39+
</script>
40+
41+
<template>
42+
<span v-if="isLoading">Loading...</span>
43+
<span v-else-if="isError">Error: {{ error.message }}</span>
44+
<!-- We can assume by this point that `isSuccess === true` -->
45+
<ul v-else>
46+
<li v-for="todo in data" :key="todo.id">{{ todo.title }}</li>
47+
</ul>
48+
<button @click="onButtonClick">Add Todo</button>
49+
</template>
50+
```
51+
52+
These three concepts make up most of the core functionality of Vue Query. The next sections of the documentation will go over each of these core concepts in great detail.

docs/config.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@
5858
"to": "adapters/solid-query"
5959
},
6060
{
61-
"label": "Vue Query (Coming Soon)",
62-
"to": "#"
61+
"label": "Vue Query",
62+
"to": "adapters/vue-query"
6363
},
6464
{
6565
"label": "Svelte Query (Coming Soon)",

examples/vue/basic/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules
2+
.DS_Store
3+
dist
4+
dist-ssr
5+
*.local
6+
package-lock.json

examples/vue/basic/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Basic example
2+
3+
To run this example:
4+
5+
- `npm install` or `yarn`
6+
- `npm run dev` or `yarn dev`

examples/vue/basic/index.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Vue Query Example</title>
7+
</head>
8+
<body>
9+
<div id="app"></div>
10+
<script type="module" src="/src/main.ts"></script>
11+
</body>
12+
</html>

examples/vue/basic/package.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "@tanstack/query-example-vue-basic",
3+
"private": true,
4+
"scripts": {
5+
"dev": "vite",
6+
"build": "vite build",
7+
"build:dev": "vite build -m development",
8+
"serve": "vite preview"
9+
},
10+
"dependencies": {
11+
"vue": "3.2.39",
12+
"@tanstack/vue-query": "^4.9.0"
13+
},
14+
"devDependencies": {
15+
"@vitejs/plugin-vue": "3.1.0",
16+
"typescript": "4.8.4",
17+
"vite": "3.1.4"
18+
}
19+
}

examples/vue/basic/src/App.vue

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<script lang="ts">
2+
import { defineComponent, ref } from "vue";
3+
4+
import Posts from "./Posts.vue";
5+
import Post from "./Post.vue";
6+
7+
export default defineComponent({
8+
name: "App",
9+
components: { Posts, Post },
10+
setup() {
11+
const visitedPosts = ref(new Set());
12+
const isVisited = (id: number) => visitedPosts.value.has(id);
13+
14+
const postId = ref(-1);
15+
const setPostId = (id: number) => {
16+
visitedPosts.value.add(id);
17+
postId.value = id;
18+
};
19+
20+
return {
21+
isVisited,
22+
postId,
23+
setPostId,
24+
};
25+
},
26+
});
27+
</script>
28+
29+
<template>
30+
<h1>Vue Query - Basic</h1>
31+
<p>
32+
As you visit the posts below, you will notice them in a loading state the
33+
first time you load them. However, after you return to this list and click
34+
on any posts you have already visited again, you will see them load
35+
instantly and background refresh right before your eyes!
36+
<strong>
37+
(You may need to throttle your network speed to simulate longer loading
38+
sequences)
39+
</strong>
40+
</p>
41+
<Post v-if="postId > -1" :postId="postId" @setPostId="setPostId" />
42+
<Posts v-else :isVisited="isVisited" @setPostId="setPostId" />
43+
</template>

examples/vue/basic/src/Post.vue

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<script lang="ts">
2+
import { defineComponent } from "vue";
3+
import { useQuery } from "@tanstack/vue-query";
4+
5+
import { Post } from "./types";
6+
7+
const fetcher = async (id: number): Promise<Post> =>
8+
await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`).then(
9+
(response) => response.json()
10+
);
11+
12+
export default defineComponent({
13+
name: "PostDetails",
14+
props: {
15+
postId: {
16+
type: Number,
17+
required: true,
18+
},
19+
},
20+
emits: ["setPostId"],
21+
setup(props) {
22+
const { isLoading, isError, isFetching, data, error } = useQuery(
23+
["post", props.postId],
24+
() => fetcher(props.postId)
25+
);
26+
27+
return { isLoading, isError, isFetching, data, error };
28+
},
29+
});
30+
</script>
31+
32+
<template>
33+
<h1>Post {{ postId }}</h1>
34+
<a @click="$emit('setPostId', -1)" href="#"> Back </a>
35+
<div v-if="isLoading" class="update">Loading...</div>
36+
<div v-else-if="isError">An error has occurred: {{ error }}</div>
37+
<div v-else-if="data">
38+
<h1>{{ data.title }}</h1>
39+
<div>
40+
<p>{{ data.body }}</p>
41+
</div>
42+
<div v-if="isFetching" class="update">Background Updating...</div>
43+
</div>
44+
</template>
45+
46+
<style scoped>
47+
.update {
48+
font-weight: bold;
49+
color: green;
50+
}
51+
</style>

examples/vue/basic/src/Posts.vue

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<script lang="ts">
2+
import { defineComponent } from "vue";
3+
import { useQuery } from "@tanstack/vue-query";
4+
5+
import { Post } from "./types";
6+
7+
const fetcher = async (): Promise<Post[]> =>
8+
await fetch("https://jsonplaceholder.typicode.com/posts").then((response) =>
9+
response.json()
10+
);
11+
12+
export default defineComponent({
13+
name: "PostsList",
14+
props: {
15+
isVisited: {
16+
type: Function,
17+
required: true,
18+
},
19+
},
20+
emits: ["setPostId"],
21+
setup() {
22+
const { isLoading, isError, isFetching, data, error, refetch } = useQuery(
23+
["posts"],
24+
fetcher
25+
);
26+
27+
return { isLoading, isError, isFetching, data, error, refetch };
28+
},
29+
});
30+
</script>
31+
32+
<template>
33+
<h1>Posts</h1>
34+
<div v-if="isLoading">Loading...</div>
35+
<div v-else-if="isError">An error has occurred: {{ error }}</div>
36+
<div v-else-if="data">
37+
<ul>
38+
<li v-for="item in data" :key="item.id">
39+
<a
40+
@click="$emit('setPostId', item.id)"
41+
href="#"
42+
:class="{ visited: isVisited(item.id) }"
43+
>{{ item.title }}</a
44+
>
45+
</li>
46+
</ul>
47+
</div>
48+
</template>
49+
50+
<style scoped>
51+
.visited {
52+
font-weight: bold;
53+
color: green;
54+
}
55+
</style>

examples/vue/basic/src/main.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { createApp } from "vue";
2+
import { VueQueryPlugin } from "@tanstack/vue-query";
3+
4+
import App from "./App.vue";
5+
6+
createApp(App).use(VueQueryPlugin).mount("#app");

examples/vue/basic/src/shims-vue.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
declare module "*.vue" {
2+
import { DefineComponent } from "vue";
3+
const component: DefineComponent<{}, {}, any>;
4+
export default component;
5+
}

examples/vue/basic/src/types.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export interface Post {
2+
userId: number;
3+
id: number;
4+
title: string;
5+
body: string;
6+
}

examples/vue/basic/tsconfig.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"compilerOptions": {
3+
"target": "esnext",
4+
"module": "esnext",
5+
"moduleResolution": "node",
6+
"strict": true,
7+
"jsx": "preserve",
8+
"sourceMap": true,
9+
"resolveJsonModule": true,
10+
"esModuleInterop": true,
11+
"lib": ["esnext", "dom"],
12+
"types": ["vite/client"]
13+
},
14+
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
15+
}

examples/vue/basic/vite.config.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { defineConfig } from "vite";
2+
import vue from "@vitejs/plugin-vue";
3+
4+
// https://vitejs.dev/config/
5+
export default defineConfig({
6+
plugins: [vue()],
7+
optimizeDeps: {
8+
include: ["remove-accents"],
9+
exclude: ["vue-query", "vue-demi"],
10+
},
11+
});

packages/vue-query/.eslintrc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"parserOptions": {
3+
"project": "./tsconfig.lint.json",
4+
"sourceType": "module"
5+
},
6+
"rules": {
7+
"react-hooks/rules-of-hooks": "off"
8+
}
9+
}

0 commit comments

Comments
 (0)