iT邦幫忙

2024 iThome 鐵人賽

DAY 14
0
Modern Web

Svelte 的奇妙冒險系列 第 14

[Svelte 的奇妙冒險] Day 14 - TanStack Query

  • 分享至 

  • xImage
  •  

如果是有在寫 React 的讀者多多少少都會聽過 React Query 這個 library ,它在 v4 之後就改名為 TanStack Query 並且開始支援其他框架。

使用前請先安裝

pnpm add @tanstack/svelte-query
# yarn add  @tanstack/svelte-query
# npm install @tanstack/svelte-query

為什麼我們需要 TanStack Query

先來簡單介紹 TanStack Query,它是一個「管理非同步狀態」的 library ,以 query 來說我通常會是給他一個 queryFn 後會去執行那個 async function ,接著把 response 放進一個「狀態」裡,接下來就可以從那個狀態拿出 dataisSuccessisPending 等等幫助我們管理非同步狀態的事情。

在使用前會需要在 src/routes/+layout.svelte 加上:

<script>
	import { browser } from '$app/environment';
	import { QueryClient, QueryClientProvider } from '@tanstack/svelte-query';
	import '../app.css';

	const queryClient = new QueryClient({
		defaultOptions: {
			queries: {
				enabled: browser
			}
		}
	});
</script>

<QueryClientProvider client={queryClient}>
	<slot />
</QueryClientProvider>

然後我們就能在 +page.svelte 中使用

<script lang="ts">
	import { createQuery } from '@tanstack/svelte-query';

	interface Todo {
		id: number;
		title: string;
		completed: boolean;
		userId: number;
	}

	const fetchData = async (): Promise<Todo[]> => {
		const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
		const data = await response.json();
		return data;
	};

	const query = createQuery<Todo[]>({
		queryKey: ['queryKey'],
		queryFn: async () => await fetchData()
	});
</script>

<div class="p-12">
	{#if $query.isPending}
		Loading...
	{/if}

	{#if $query.isError}
		Error: {$query.error.message}
	{/if}

	{#if $query.isSuccess}
		<div class="flex items-center flex-col gap-4">
			{#each $query.data as todo}
				<div class="card bg-primary text-primary-content w-96">
					<div class="card-body">
						<h2 class="card-title">{todo.title}</h2>
						<p>{todo.completed ? 'Completed' : 'Not Completed'}</p>
					</div>
				</div>
			{/each}
		</div>
	{/if}
</div>


而在 @tanstack/svelte-query 中的那個「狀態」是用 Svelte 的 store 來實作,但因為 Svelte 5 裡有它的替代品所以就剛好沒講到了,簡單來說如果我們要使用或者該說訂閱某個 store ,就直接加一個 $ 變成 $store ,這樣子當 store 有變化時我們也能拿到最新的值。

那你可能會想那為什麼不用 {#await} 就好?

	{#await fetchData()}
		Loading...
	{:then data}
		<div class="flex items-center flex-col gap-4">
			{#each data as todo}
				<div class="card bg-primary text-primary-content w-96">
					<div class="card-body">
						<h2 class="card-title">{todo.title}</h2>
						<p>{todo.completed ? 'Completed' : 'Not Completed'}</p>
					</div>
				</div>
			{/each}
		</div>
	{:catch error}
		Error: {error.message}
	{/await}

當然如果需求只是在 Day 3 時說到的「在 component mounted時打一次 api 然後渲染」這種情況,的確用 {#await} 會簡單很多。

但是如果你想要「手動 refetch」、「將 API result 的資料拿出來做為他用」或者「其他地方需要因應 query 的 status 而有其他行為」之類的需求,使用 {#await} 就會顯得有那麼一點麻煩,你可能需要額外宣告一些 $state 來達成這些需求。

再加上 createQuery 有其他方便的功能,像是 auto refetch 、cache 的控制

目前的一些需要注意的地方

畢竟 @tanstack/svelte-query 還算是比較新的 library 且 Svelte 5 還沒正式發布,所以在整合起來還是有一些地方需要注意。

像是如果 createQuery 的某些值來執行需要依靠 $state 來寫的話還是沒辦法正常運行的,

如果我想要做一個 button 來控制什麼時候才要開始進行 fetching

利用 rune 的寫法會發現沒辦法起作用

// ❌ 不要這樣寫

let enabled = $state(false);

	let query = $derived(
		createQuery<Todo[]>({
			queryKey: ['queryKey'],
			enabled,
			queryFn: async () => await fetchData()
		})
	);

	$effect(() => {
		console.log('status', $query.status);
	});

雖然我自己覺得的原因是目前 v5 的 reactivity 系統與 v4 store 整合起來還是有點割裂感

必須改為使用 v4 的 store 的方式來撰寫

	let enabled = writable(false);

	let query = createQuery<Todo[]>(
		derived(enabled, (enabled) => ({
			queryKey: ['queryKey'],
			enabled,
			queryFn: async () => await fetchData()
		}))
	);

	$effect(() => {
		console.log('data', enabled);
		console.log('status', $query.status);
	});
<button
	class="btn btn-primary"
	onclick={() => {
		enabled.update((prev) => !prev);
	}}
>
	toggle
</button>

這或許是這個 library 中目前比較困擾人的點吧QQ


參考資料

source code

https://github.com/toddLiao469469/30days-for-svelte5/blob/main/src/routes/day14/%2Bpage.svelte


上一篇
[Svelte 的奇妙冒險] Day 13 - runed,一個好用的 utility library
下一篇
[Svelte 的奇妙冒險] Day 15 - SvelteKit 介紹
系列文
Svelte 的奇妙冒險30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言