π¦ Zustand Utility For Creating Async Slice easily in TypeScript!
The asyncSlice
function automatically creates and manages various states inside the Zustand Store by simply passing the slice name to name
and the asynchronous function to asyncFn
.
It even provides full support for TypeScript. π₯
It minimizes the hassle for developers to manually write types, ensuring a smooth developer experience
If we pass hello
, it generates like that.
yarn add zustand-async-slice
name: hello
isHelloFetching: boolean
isHelloError: boolean
helloData: Data | undefined
- type parameter
Data
is inferred return type ofasyncFn
- type parameter
runHello: (params: Params, callbacks?: Callbacks) => void
runHello: (callbacks?: Callbacks) => void
- type parameter
Params
should be passed second argument ofasyncSlice
- is there no params? then no arg function will be generated βΌ
- callbacks are
onRun
,onSettled
,onSuccess
,onError
. You can pass callbacks from the caller at the runtime or definition of the async slice.
- type parameter
runHelloAsync: (params: Params) => Promise<Data>
runHelloAsync: () => Promise<Data>
- returnning
Promise<Data>
function is available too
- returnning
Let's create a async slice named Hello
by passing Hello
string to name
parameter.
const helloSlice = asyncSlice<MyStoreState>()({
name: 'hello',
asyncFn: async ({ get, set }) => {
await new Promise((r) => setTimeout(r, 3000)); // wait 3 seconds
return 1;
},
// on asyncFn has been called
onRun: ({ get, set }) => {},
// on asyncFn has been completed as success or failure
onSettled: ({ get, set, data, error, isError, isSuccess }) => {},
// on asyncFn has been completed as success
onSuccess: ({ get, set, data }) => {},
// on asyncFn has been completed as error
onError: ({ get, set, error }) => {},
});
Note
Yes, get
and set
are those in Zustand store API.
The type of get
and set
are inferred from first type parameter of asyncSlice
(MyStoreState
).
const helloSlice = asyncSlice<MyStoreState, { arg1: number; arg2: string }>()({
name: 'Hello',
asyncFn: async ({ arg1, arg2 }, { get, set }) => {
await new Promise((r) => setTimeout(r, 3000)); // wait 3 seconds
return 1;
},
onRun: ({ params, get, set }) => {},
onSettled: ({ params, get, set, data, error, isError, isSuccess }) => {},
onSuccess: ({ params, get, set, data }) => {},
onError: ({ params, get, set, error }) => {},
});
Check that the parameter type of the async function is defined as the second argument of asyncSlice
and that params
are added to each callback function.
Tip
Why currying? ()(...)
> Read on Zustand TS docs
import type { WithAsyncState } from 'zustand-async-slice';
export type MyStoreState = { age: number };
export const useMyStore = create<WithAsyncState<typeof helloSlice>>()((set, get, store) => ({
age: 0,
...helloSlice(set, get, store), // Inject
}));
Thanks to WrapAsyncState
, we can simply pass the slice's type to it, and without needing to redefine the existing Store's type using &
, we can just pass it as a type argument to create
.
import { asyncSlice, WithAsyncState } from 'zustand-async-slice';
type MyState = { age: number; };
const helloSlice = asyncSlice<MyState>()({
name: 'hello',
asyncFn: async ({ set }) => { // can be async or not
set({ age: 1 });
return 1;
},
});
const useMyStore = create<WithAsyncState<typeof helloSlice>>((...s) => ({
age: 0,
...helloSlice(...s),
}));