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<P> {
signal: AbortSignal;
param: P;
}
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 };
});
param
You can pass a parameter to the request. This parameter will be passed to the request function. Each call of execute
will override the previous parameter. refresh
will use the last parameter.
const useUserStore = createStore("user", ({ request }) => {
// You can type the param as you want or use `VaxeeRequestParams<number>` type
const user = request(({ param }: { param: number }) =>
fetch(`/users/${param}`).then((res) => res.json())
);
return { user };
});
<script setup>
const { data: user, execute: executeUser } = useUserStore("user");
onMounted(() => {
executeUser(1);
});
</script>
To add a default parameter, you can pass it to the request
function with destructuring.
const useUserStore = createStore("user", ({ request }) => {
const user = request(({ param = 1 }: { param: number }) =>
fetch(`/users/${param}`).then((res) => res.json())
);
return { user };
});
Options
You can pass options to the request
function to customize the behavior.
interface VaxeeRequestOptions {
mode?: "auto" | "lazy" | "manual";
watch?: WatchSource[];
onError?: <E = unknown>(error: E) => any;
}
mode
auto
- The request will be sent automatically on server-side, if available, withonServerPrefetch
hook. Also the request will be sent automatically on the client-side if did not send on the server-side.client
- The request will be sent automatically only on the client-side.manual
- If you want control the request manually, you can set themode
tomanual
and request won't be sent automatically.
Default:
auto
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 };
});
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,
} = await 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
// ❌ it will work but you'll get the same if mode is "auto"
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 promiseable 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",
}