Request
Vaxee provides a request
function to fetch data and store it in the store. Also request
provides a way to cache the data and several options to enhance the data fetching process. Cache is stored in the store and is shared between all components that use the store. That means if you fetch the data in one component, it will be available in another component without fetching it again.
TIP
If you are using server-side rendering (SSR), it is recommended to read the SSR section to avoid hydration errors.
Create a Request
To create a request, use the request
function from the createStore
callback.
import { createStore } from "vaxee";
import { fetchUser } from "../api/user";
export const useUserStore = createStore("user", ({ request }) => {
const user = request(() => fetchUser(1));
return {
user,
};
});
This small example includes caching the data, error handling and loading states. Now each call of useUserStore
won't trigger a new request to the server. Instead, it will return the cached data.
Parameters
The request
function accepts a function that returns a promise. This function provides arguments that can be used to customize the behavior of the request.
interface VaxeeRequestParams {
signal: AbortSignal;
}
signal
An AbortSignal object is used to automatically abort the request when the request is sent multiple times.
const useUsersStore = createStore("users", ({ request }) => {
const users = request(({ signal }) =>
fetch("/users", { signal }).then((res) => res.json())
);
return { users };
});
Options
You can pass options to the request
function to customize the behavior.
interface VaxeeRequestOptions {
sendManually?: boolean;
watch?: WatchSource[];
sendOnServer?: boolean;
onError?: <E = unknown>(error: E) => any;
}
sendManually
By default, the request is sent automatically when the store is used. You can pass a boolean
property that determines whether the request should be sent manually.
Default is
false
.
const useUserStore = createStore("user", ({ request }) => {
const user = request(() => fetchUser(), {
sendManually: true,
});
return { user };
});
And then you can call the execute
function to send the request manually.
<script setup>
const {
user: { execute },
} = useUserStore();
onMounted(() => {
execute();
});
</script>
watch
You can pass an array of state
objects or getter
properties or a function that returns a value to watch. If the value changes, the request will be refreshed.
const useUserStore = createStore("user", ({ request, state }) => {
const token = state("");
const user = request(
() => {
if (!token.value) return null;
return fetchUser();
},
{
watch: [token],
}
);
return { token, user };
});
sendOnServer
By default, the request is automatically sent on the server-side if available. You can pass a boolean
property that determines whether the request should be sent on the server-side. If false
, the request will be automatically sent only on the client-side.
Default is
true
.
const useUserStore = createStore("user", ({ request }) => {
const user = request(() => fetchUser(), {
sendOnServer: false,
});
return { user };
});
onError
You can pass a function that will be called when an error occurs.
const useUserStore = createStore("user", ({ request }) => {
const user = request(() => fetchUser(), {
onError: (error) => {
console.error(error);
},
});
return { user };
});
Use a Request
Let's use this request in a component.
<script setup>
import { useUserStore } from "../stores/user";
const {
// user, userStatus, userError are still reactive
data: user,
error: userError,
status: userStatus,
execute: executeUser,
refresh: refreshUser,
suspense: suspenseUser,
onSuccess: onUserSuccess,
onError: onUserError,
} = useUserStore("user");
onUserSuccess((user) => {
console.log("User fetched successfully", user);
});
onUserError((error) => {
console.error("User fetch failed", error);
});
onMounted(() => {
setInterval(() => {
refreshUser();
}, 5000);
});
// Or you can use it directly:
// const store = useUserStore.reactive();
// store.user.data
onServerPrefetch(async () => {
await suspenseUser();
});
</script>
<template>
<div v-if="userStatus === 'fetching'">Loading...</div>
<div v-else-if="userStatus === 'error'">Error: {{ userError.message }}</div>
<div v-else>
Refreshing: {{ userStatus === "refreshing" }}<br />
<p>User ID: {{ user.id }}</p>
<p>User Name: {{ user.name }}</p>
<p>User Email: {{ user.email }}</p>
</div>
</template>
Properties
The request
function returns an object with the following properties:
data
- The data returned from the request. It's aref
object.error
- The error returned from the request. It's aref
object.status
- The status of the request. It's aref
object. It can be one of the following values:execute
- A function that sends the request and clears the data.refresh
- A function that refreshes the request without clearing the data.suspense
- A function that waiting for the promise to resolve. It's useful for server-side rendering (SSR).onSuccess
- A function that is called when the request is successful.onError
- A function that is called when the request fails.
enum VaxeeRequestStatus {
NotFetched = "not-fetched",
Fetching = "fetching",
Refreshing = "refreshing",
Error = "error",
Success = "success",
}
SSR
If you are using some server-side rendering (SSR) framework, you can use the suspense
function to wait the data before rendering the component.
WARNING
If you are not using suspense
/execute
/refresh
in SSR to wait for the data to resolve, the request will be send twice due to the client-side rendering. Use some function to wait to the request promise resolve, it won't be fetched again on the client-side.
TIP
If you want to fetch the data only on the client-side, you need to disable the sendOnServer
option.
TIP
The suspense
function is not responsible for fetching data, even if you call it multiple times. Its purpose is to allow you to wait for the promise inside the request
function to resolve. To fetch the data, you need to call the execute
or refresh
function.
import { useUserStore } from "../stores/user";
const {
user: { suspense: userSuspense },
} = useUserStore();
/**
* Wait the data resolving on the server only when the component is visible on the rendered page.
* It won't be fetched on the client side after router navigation.
*
* @see https://vuejs.org/api/composition-api-lifecycle#onserverprefetch
*/
onServerPrefetch(async () => {
await userSuspense();
});
/**
* Wait the data resolving every time the component is rendered.
*
* @see https://vuejs.org/guide/built-ins/suspense.html#async-dependencies
*/
await userSuspense();