Last active
June 27, 2024 01:23
-
-
Save kyle-mccarthy/cae2df1089c71b9d6f5eb55992a15474 to your computer and use it in GitHub Desktop.
namespaced slices in zustand
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { GetState, PartialState, SetState, State } from "zustand"; | |
const noop = (..._: unknown[]): void => { | |
/* noop */ | |
}; | |
export type Setter<T extends State> = ( | |
s: Partial<T> | ((prev: T) => Partial<T>), | |
replace?: boolean | |
) => void; | |
export type Factory<T extends State> = (set: Setter<T>, get: () => T) => T; | |
// Zustand recommnds splitting the store into separate "slices", but doesn't | |
// really provide a great way to do this. [^slices] | |
// | |
// This allows for easily creating slices that are encapsulated as an object in | |
// the primary store. `createSlice` does this by re-scoping the get and set | |
// functions to the slice based on the property name passed as the first arg. | |
// | |
// # Example | |
// | |
// ## Create the slice for the primary store | |
// ``` | |
// interface UserSlice { | |
// id?: number; | |
// email?: string; | |
// setId: (id: number) => void; | |
// setEmail: (email: string) => void; | |
// } | |
// | |
// const userFactory: Factory<AddressSlice> = (set, _get) => ({ | |
// setId: (id: number) => set({ id }), | |
// setEmail: (email: string) => set({ email }), | |
// }); | |
// | |
// const createUserSlice = createSlice("user", userFactory); | |
// ``` | |
// | |
// ## Create the store and include your slice | |
// ``` | |
// import { create } from "zustand"; | |
// | |
// interface Store { | |
// user: UserSlice; | |
// ... | |
// } | |
// | |
// const store = create((set, get) => ({ | |
// user: createUserSlice(set, get), | |
// })); | |
// ``` | |
// | |
// [^slices]: https://github.com/pmndrs/zustand/wiki/Splitting-the-store-into-separate-slices | |
export const createSlice = < | |
P extends keyof Z, | |
S extends State, | |
Z extends { [k in P]: S } | |
>( | |
property: P, | |
factory: Factory<S> | |
): ((set: SetState<Z>, get: GetState<Z>) => S) => { | |
return (set, get) => { | |
const getter = (): S => { | |
return get()[property]; | |
}; | |
const setter: Setter<S> = (arg, replace) => { | |
const prev = get()[property]; | |
const next = typeof arg === "function" ? arg(prev) : arg; | |
if (next === prev) { | |
return noop(); | |
} | |
if (replace) { | |
return set({ [property]: next as S } as PartialState<Z>); | |
} | |
return set({ | |
[property]: { | |
...prev, | |
...next, | |
} as S, | |
} as PartialState<Z>); | |
}; | |
return factory(setter, getter); | |
}; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I've tried using this and being new to Zustand I can't get it to work fully, as the 4 imports are all deprecated.
import { GetState, PartialState, SetState, State } from "zustand";
When I use it the 2nd one of the 2 below works, however as soon as I wrap immer, devtools or even persist around it (the first one), I get red squiggle lines. Would it be possible for you to update the above code to use the latest version of Zustand?
export const useStoreFactory = createSelectors(
create(
immer((set, get) => ({
user: createUserSlice(set, get),
userSettings: createUserSettingsSlice(set, get),
}))
)
);
export const useStoreFactoryWorks = createSelectors(
create((set, get) => ({
user: createUserSlice(set, get),
userSettings: createUserSettingsSlice(set, get),
}))
);