-
-
Save kyle-mccarthy/cae2df1089c71b9d6f5eb55992a15474 to your computer and use it in GitHub Desktop.
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); | |
}; | |
}; |
@kyle-mccarthy line 82:
const prev: Z[P] Spread types may only be created from object types
Zustand used to treat all state as an object. It looks like that changed recently.
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),
}))
);
@kyle-mccarthy line 82: