|
1 |
| -# Introduction |
| 1 | +# Defining Data Loaders |
2 | 2 |
|
3 |
| -## Defining a Loader |
| 3 | +In order to use data loaders, you need to define them first. Data loaders themselves are the composables returned by the different `defineLoader` functions. Each loader definition is specific to the `defineLoader` function used. For example, `defineBasicLoader` expects an async function as the first argument while `defineColadaLoader` expects an object with a `query` function. All loaders should allow to pass an async function that can throw errors, and `NavigationResult`. |
4 | 4 |
|
5 |
| -Should not contain any side-effects besides the data fetching. |
| 5 | +Any composables returned by _any_ `defineLoader` function share the same signature: |
| 6 | + |
| 7 | +```vue twoslash |
| 8 | +<script lang="ts"> |
| 9 | +import 'unplugin-vue-router/client' |
| 10 | +import './typed-router.d' |
| 11 | +// ---cut--- |
| 12 | +import { defineBasicLoader } from 'unplugin-vue-router/data-loaders/basic' |
| 13 | +import { getUserById } from '../api' |
| 14 | +
|
| 15 | +export const useUserData = defineBasicLoader('/users/[id]', async (route) => { |
| 16 | + return getUserById(route.params.id) |
| 17 | +}) |
| 18 | +</script> |
| 19 | +
|
| 20 | +<script setup lang="ts"> |
| 21 | +// hello // [!code focus:7] |
| 22 | +const { |
| 23 | + data: user, // the data returned by the loader |
| 24 | + isLoading, // a boolean indicating if the loader is fetching data |
| 25 | + error, // an error object if the loader failed |
| 26 | + reload, // a function to refetch the data without navigating |
| 27 | +} = useUserData() |
| 28 | +</script> |
| 29 | +``` |
| 30 | + |
| 31 | +**But they are not limited by it!** For example, the `defineColadaLoader` function returns a composable with a few more properties like `status` and `refresh`. Because of this it's important to refer to the documentation of the specific loader you are using. |
| 32 | + |
| 33 | +This page will guide you through the **foundation** of defining data loaders, no matter their implementation. |
| 34 | + |
| 35 | +## The loader function |
| 36 | + |
| 37 | +The loader function is the _core_ of data loaders. They are asynchronous functions that return the data you want to expose in the `data` property of the returned composable. |
| 38 | + |
| 39 | +### The `to` argument |
| 40 | + |
| 41 | +The `to` argument represents the location object we are navigating to. It should be used as the source of truth for all data fetching parameters. |
| 42 | + |
| 43 | +```ts twoslash |
| 44 | +import 'unplugin-vue-router/client' |
| 45 | +import './typed-router.d' |
| 46 | +import { defineBasicLoader } from 'unplugin-vue-router/data-loaders/basic' |
| 47 | +import { getUserById } from '../api' |
| 48 | +// ---cut--- |
| 49 | +export const useUserData = defineBasicLoader('/users/[id]', async (to) => { |
| 50 | + const user = await getUserById(to.params.id) |
| 51 | + // here we can modify the data before returning it |
| 52 | + return user |
| 53 | +}) |
| 54 | +``` |
| 55 | + |
| 56 | +By using the route location to fetch data, we ensure a consistent relationship between the data and the URL, **improving the user experience**. |
| 57 | + |
| 58 | +### Side effects |
| 59 | + |
| 60 | +It's important to avoid side effects in the loader function. Don't call `watch`, or create reactive effects like `ref`, `toRefs()`, `computed`, etc. |
| 61 | + |
| 62 | +## Accessing Global Properties |
| 63 | + |
| 64 | +In the loader function, you can access global properties like the router instance, a store, etc. This is because using `inject()` within the loader function **is possible**. Since loaders are asynchronous, make sure you are using the `inject` function **before any `await`**: |
| 65 | + |
| 66 | +```ts twoslash |
| 67 | +import 'unplugin-vue-router/client' |
| 68 | +import './typed-router.d' |
| 69 | +import { defineBasicLoader } from 'unplugin-vue-router/data-loaders/basic' |
| 70 | +import { getUserById } from '../api' |
| 71 | +// ---cut--- |
| 72 | +import { inject } from 'vue' |
| 73 | +import { useSomeStore } from '@/stores' |
| 74 | + |
| 75 | +export const useUserData = defineBasicLoader('/users/[id]', async (to) => { |
| 76 | + // ✅ This will work |
| 77 | + const injectedValue = inject('key') // [!code ++] |
| 78 | + const store = useSomeStore() // [!code ++] |
| 79 | + |
| 80 | + const user = await getUserById(to.params.id) |
| 81 | + // ❌ These won't work |
| 82 | + const injectedValue2 = inject('key') // [!code error] |
| 83 | + const store2 = useSomeStore() // [!code error] |
| 84 | + // ... |
| 85 | + return user |
| 86 | +}) |
| 87 | +``` |
| 88 | + |
| 89 | +<!-- |
| 90 | +Why doesn't this work? |
| 91 | + // @error: Custom error message |
| 92 | +--> |
| 93 | + |
| 94 | +## Options |
| 95 | + |
| 96 | +All |
6 | 97 |
|
7 | 98 | ## Connecting a loader to a page
|
8 | 99 |
|
|
0 commit comments