Skip to content

Commit

Permalink
refactor!: use @tanstack/vue-query instead of useAsyncData (#656)
Browse files Browse the repository at this point in the history
* refactor!: use `vue-query` instead of `useAsyncData`

* revert commenting out sentry

* changeset
  • Loading branch information
qwerzl authored Dec 20, 2024
1 parent 1f1e4c7 commit 6db4416
Show file tree
Hide file tree
Showing 18 changed files with 189 additions and 88 deletions.
5 changes: 5 additions & 0 deletions .changeset/dull-icons-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"enspire": patch
---

Refactor internal strategy for making API requests
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
shamefully-hoist=true
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,10 @@ import { enums } from '~/components/custom/enum2str'
const { toast } = useToast()
const { data } = await useAsyncData<ClassroomData[]>('classroomStatuses', () => {
return $fetch<ClassroomData[]>(`/api/reservation/classroomId`, {
headers: useRequestHeaders(),
method: 'GET',
})
const { data, suspense } = useQuery<ClassroomData[]>({
queryKey: ['/api/reservation/classroomId'],
})
await suspense()
if (!data.value) {
toast({
Expand All @@ -32,12 +30,10 @@ else {
data.value = data.value.sort((a: any, b: any) => a.name < b.name ? -1 : 1)
}
const { data: clubs } = await useAsyncData<AllClubs>('allClubs', () => {
return $fetch<AllClubs>(`/api/user/all_clubs`, {
headers: useRequestHeaders(),
method: 'GET',
})
const { data: clubs, suspense: clubsSuspense } = useQuery<AllClubs>({
queryKey: ['/api/user/all_clubs'],
})
await clubsSuspense()
if (!clubs.value) {
toast({
Expand Down
12 changes: 3 additions & 9 deletions components/custom/CAS/Record/NewActivityRecord.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@ const { toast } = useToast()
const selectedClub = ref<string>()
definePageMeta({
middleware: ['auth'],
})
const isLoading = ref(false)
const formSchema = toTypedSchema(z.object({
Expand All @@ -51,12 +47,10 @@ const formSchema = toTypedSchema(z.object({
sTime: z.number().min(0).max(5),
}))
const { data } = await useAsyncData<AllClubs>('allClubsWithMemberships', async () => {
return await $fetch<AllClubs>('/api/user/all_clubs?includeMemberships=true', {
headers: useRequestHeaders(),
method: 'GET',
})
const { data, suspense } = useQuery<AllClubs>({
queryKey: ['/api/user/all_clubs'],
})
await suspense()
if (!data.value) {
throw createError({
Expand Down
28 changes: 10 additions & 18 deletions components/custom/CAS/Record/ViewMyActivityRecords.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<script setup lang="ts">
import type { Ref } from 'vue'
import type { MyRecords } from '~/types/api/cas/record/my'
import type { AllClubs } from '~/types/api/user/all_clubs'
import {
Card,
CardContent,
Expand All @@ -16,36 +18,26 @@ import {
SelectValue,
} from '@/components/ui/select'
import { Toaster } from '~/components/ui/toast'
import type { MyRecords } from '~/types/api/cas/record/my'
import type { AllClubs } from '~/types/api/user/all_clubs'
import { columns } from './view-activity-records/columns'
import DataTable from './view-activity-records/DataTable.vue'
import { useQuery } from '@tanstack/vue-query';
const props = defineProps<{
refreshWatcher: Ref<boolean>
}>()
definePageMeta({
middleware: ['auth'],
})
const selectedClub = ref<string>()
const isLoading = ref(false)
const { data: clubs } = await useAsyncData<AllClubs>('allClubs', () => {
return $fetch<AllClubs>(`/api/user/all_clubs`, {
headers: useRequestHeaders(),
method: 'GET',
})
const { data: clubs, suspense } = useQuery<AllClubs>({
queryKey: ['/api/user/all_clubs'],
})
await suspense()
const { data, refresh } = await useAsyncData<MyRecords>('allRequests', () => {
return selectedClub.value
? $fetch<MyRecords>(`/api/cas/record/my?club=${selectedClub.value}`, {
headers: useRequestHeaders(),
method: 'GET',
})
: Promise.resolve({ total: 0, data: [] })
const { data, refetch: refresh } = useQuery<MyRecords>({
queryKey: [`/api/cas/record/my?club=`, selectedClub],
enabled: !!selectedClub.value,
placeholderData: { total: 0, data: [] },
})
async function onRefresh() {
Expand Down
7 changes: 3 additions & 4 deletions components/custom/Index/announcements.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@
import type { Announcements } from '~/types/payloadcms/announcements'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
const { data } = await useAsyncData<Announcements>('allRequests', () => {
return $fetch('/api/cms/api/announcements', {
method: 'GET',
})
const { data, suspense } = useQuery<Announcements>({
queryKey: ['/api/cms/api/announcements'],
})
await suspense()
</script>

<template>
Expand Down
25 changes: 10 additions & 15 deletions components/custom/sidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,21 @@
import type { AllClubs } from '~/types/api/user/all_clubs'
import { Button } from '@/components/ui/button'
import { cn } from '@/lib/utils'
import { useClerk } from 'vue-clerk'
const clerk = useClerk()
const route = useRoute()
const isPresidentOrVicePresident = ref(false)
useState('isEnspireLoading').value = true
if (import.meta.client) {
if (clerk.user?.publicMetadata.binded) {
const clubs = await $fetch<AllClubs>(`/api/user/all_clubs`, {
headers: useRequestHeaders(),
method: 'GET',
})
if (clubs) {
useState('isEnspireLoading').value = false
if (clubs.president.length !== 0 || clubs.vice.length !== 0) {
isPresidentOrVicePresident.value = true
}
}
const { data: clubs, suspense } = useQuery<AllClubs>({
queryKey: ['/api/user/all_clubs'],
})
await suspense()
if (clubs.value) {
useState('isEnspireLoading').value = false
if (clubs.value.president.length !== 0 || clubs.value.vice.length !== 0) {
isPresidentOrVicePresident.value = true
}
}
</script>
Expand Down Expand Up @@ -64,7 +59,7 @@ if (import.meta.client) {
</Button>
</NuxtLink>
<NuxtLink v-if="[0, 1, 5, 6].includes(new Date().getMonth())" to="/cas/rating">
<Button :variant="route.name === 'cas-rating' ? 'secondary' : 'ghost'" class="w-full justify-start mt-1">
<Button :variant="route.name === 'cas-rating' ? 'secondary' : 'ghost'" class="mt-1 w-full justify-start">
<Icon class="mr-2 h-4 w-4" name="material-symbols:rate-review-outline" />
期末评价
</Button>
Expand Down
2 changes: 2 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import antfu from '@antfu/eslint-config'
import pluginQuery from '@tanstack/eslint-plugin-query'

export default antfu({
typescript: {
Expand All @@ -7,6 +8,7 @@ export default antfu({
vue: true,
ignores: ['components/ui/'],
unocss: true,
...pluginQuery.configs['flat/recommended'],
}, {
rules: {
'node/prefer-global/process': 'off',
Expand Down
15 changes: 15 additions & 0 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,20 @@ export default defineNuxtConfig({
client: true,
},

imports: {
presets: [
{
from: '@tanstack/vue-query',
imports: ['useQuery'],
},
],
},

vite: {
optimizeDeps: {
exclude: ['vee-validate'],
},
},

compatibilityDate: '2024-08-31',
})
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@nuxt/content": "^2.13.4",
"@radix-icons/vue": "^1.0.0",
"@sentry/nuxt": "^8.42.0",
"@tanstack/vue-query": "^5.62.8",
"@tanstack/vue-table": "^8.20.5",
"@unovis/ts": "^1.4.5",
"@unovis/vue": "^1.4.5",
Expand Down Expand Up @@ -71,6 +72,7 @@
"@nuxtjs/google-fonts": "^3.2.0",
"@prisma/client": "^6.0.1",
"@rollup/plugin-wasm": "^6.2.2",
"@tanstack/eslint-plugin-query": "^5.62.1",
"@types/node-fetch": "^2.6.12",
"@types/sanitize-html": "^2.13.0",
"@types/uuid": "^10.0.0",
Expand Down
10 changes: 4 additions & 6 deletions pages/cas/rating.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script setup lang="ts">
import type { InternalApi } from 'nitropack'
import { Button } from '@/components/ui/button'
import { Card, CardContent } from '@/components/ui/card'
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from '@/components/ui/select'
Expand Down Expand Up @@ -28,11 +29,8 @@ const formSchema = toTypedSchema(z.object({
comment: z.string().max(100).optional(),
}))
const { data } = await useAsyncData('allRequests', () => {
return $fetch('/api/club/rating/available', {
headers: useRequestHeaders(),
method: 'GET',
})
const { data } = await useQuery<InternalApi['/api/club/rating/available']['get']>({
queryKey: ['/api/club/rating/available'],
})
if (!data.value) {
Expand Down Expand Up @@ -91,7 +89,7 @@ function onSubmitRating() {
<SelectContent>
<SelectGroup v-if="data">
<SelectItem v-for="club in data" :key="club.id" :value="String(club.id)">
{{ typeof club.name === 'object' && 'zh' in club.name! ? club.name.zh : '' }}
{{ typeof club?.name === 'object' && 'zh' in club.name! ? club.name.zh : '' }}
</SelectItem>
</SelectGroup>
</SelectContent>
Expand Down
10 changes: 4 additions & 6 deletions pages/forms/[id].vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<script setup lang="ts">
import type { Form } from '~/types/data/forms'
import { useWindowSize } from '@vueuse/core'
import { useClerk } from 'vue-clerk'
import { toast } from '~/components/ui/toast'
import type { Form } from '~/types/data/forms'
const clerk = useClerk()
Expand All @@ -16,12 +16,10 @@ useHead({
title: 'Forms | Enspire',
})
const { data } = await useAsyncData<Form[]>('classroomStatuses', async () => {
return await $fetch<Form[]>(`/api/forms/open`, {
headers: useRequestHeaders(),
method: 'GET',
})
const { data, suspense } = useQuery<Form[]>({
queryKey: ['/api/forms/open'],
})
await suspense()
if (!data.value) {
toast({
Expand Down
16 changes: 7 additions & 9 deletions pages/forms/index.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script setup lang="ts">
import type { Form } from '~/types/data/forms'
import {
Card,
CardDescription,
Expand All @@ -8,7 +9,6 @@ import {
} from '@/components/ui/card'
import Toaster from '@/components/ui/toast/Toaster.vue'
import { toast } from '~/components/ui/toast'
import type { Form } from '~/types/data/forms'
definePageMeta({
middleware: ['auth'],
Expand All @@ -18,12 +18,10 @@ useHead({
title: 'Forms | Enspire',
})
const { data } = await useAsyncData<Form[]>('classroomStatuses', async () => {
return await $fetch<Form[]>(`/api/forms/open`, {
headers: useRequestHeaders(),
method: 'GET',
})
const { data, suspense } = useQuery<Form[]>({
queryKey: ['/api/forms/open'],
})
suspense()
if (!data.value) {
toast({
Expand All @@ -34,18 +32,18 @@ if (!data.value) {
</script>

<template>
<div class="font-bold flex items-center space-x-2 text-lg">
<div class="flex items-center text-lg font-bold space-x-2">
<Icon name="material-symbols:edit-square-outline" />
<div>可填写的表单</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4 mt-2">
<div class="grid grid-cols-1 mt-2 gap-4 md:grid-cols-2 xl:grid-cols-3">
<NuxtLink v-for="club in data" :key="club.id" :to="`/forms/${club.id}`">
<Card class="hover:underline">
<CardHeader>
<CardTitle>{{ club.title }}</CardTitle>
<CardDescription>{{ club.description }}</CardDescription>
</CardHeader>
<CardFooter class="text-muted-foreground text-sm flex items-center">
<CardFooter class="flex items-center text-sm text-muted-foreground">
<div>点击填写</div>
<Icon name="material-symbols:arrow-forward" />
</CardFooter>
Expand Down
8 changes: 3 additions & 5 deletions pages/manage/statuses.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,10 @@ const selectedPeriod = ref(time2period(utc8Time.hour() * 100 + utc8Time.minute()
let dataLoaded = false
const { data } = await useAsyncData<ClassroomData[]>('classroomStatuses', async () => {
return await $fetch<ClassroomData[]>(`/api/reservation/classroomId`, {
headers: useRequestHeaders(),
method: 'GET',
})
const { data, suspense } = useQuery<ClassroomData[]>({
queryKey: ['/api/reservation/classroomId'],
})
suspense()
if (data.value) {
data.value = data.value.sort((a: any, b: any) => a.name < b.name ? -1 : 1)
Expand Down
38 changes: 38 additions & 0 deletions plugins/vue-query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type {
DehydratedState,
VueQueryPluginOptions,
} from '@tanstack/vue-query'
import {
dehydrate,
hydrate,
QueryClient,
VueQueryPlugin,
} from '@tanstack/vue-query'
import { defaultQuery } from '~/queries/default'

export default defineNuxtPlugin((nuxt) => {
const vueQueryState = useState<DehydratedState | null>('vue-query')

// Modify your Vue Query global settings here
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5000,
queryFn: defaultQuery,
},
},
})
const options: VueQueryPluginOptions = { queryClient }

nuxt.vueApp.use(VueQueryPlugin, options)

if (import.meta.server) {
nuxt.hooks.hook('app:rendered', () => {
vueQueryState.value = dehydrate(queryClient)
})
}

if (import.meta.client) {
hydrate(queryClient, vueQueryState.value)
}
})
Loading

0 comments on commit 6db4416

Please sign in to comment.