dhmk-zustand-lens's People
dhmk-zustand-lens's Issues
[Question] How do I initialize the store with props?
In this example, initProps
contains data/state only, no actions. It is also not a lens
object (this is where my problem happens I think).
I think what I need to do is initialize the props as a lens
object and merge it with the default state but I don't know how to do that. Any help would be appreciated
const useStore = create(withLenses(() => ({
...initProps,
bears: lens(set => ({
// ...bears state & actions
})),
fish: lens(set => ({
// ...fish state & actions
})),
// Placing the initial props here will set the actions to undefined
// ...initProps
})))
Typescript example has typescript error `Type ... is missing the following properties from StoreApi`
Hey.
I tried following typescript example but ran into error. I then tried to recreate the code from example but it does show an error, too.
I provided codesandbox where example typescript code is in file fromexample.ts
and my store is in file mystore.ts
After providing the store missing properties getState
, setState
, subscribe
and destroy
(which I shouldn't do) it now shows an error
Type 'StoreApi<Store>' is missing the following properties from type 'Store': id, name, nestedts
but these properties are definitely defined.
I don't understand how to type the store properly. Can you please provide direction?
how much work would it take to extend this library to support lenses over arrays
At the moment lenses only work for plain objects. I would love to extend this library to support arrays as well.
setState and Typescript, unable to update slice partially.
I use useStore.setState
to mock actions within my tests. I'm running into an issue where Typescript complains about partially updating a slice.
ie.
interface SearchSlice {
text: string;
updateText: () => void,
otherFunc: () => void
}
interface Store {
search: SearchSlice;
}
const useStore = create<Store>(withLens(() => ({ search: lens(searchSlice }))
)
//-----my test file
let mockedFun = jest.fn();
beforeEach(() => {
useStore.setState({
search: {
updateText: mockedFun
},
});
simple example but i would get a typescript error saying
Type '{ updateText: Mock<any, any>; }' is missing the following properties from type 'SearchSlice': otherFunc, text
Any ideas on how to work fix this or workaround this? (excluding always supplying properties or ignoring the error)
Thanks in advance
Just wanted to say thanks for this project
Sorry for opening an issue for this when really there's no issue to be reported, but just wanted to say thanks for building this and sharing it here, just what I needed for my project and it works wonderfully.
Setting field of type boolean to true/false raises type error
This
export const test = create<{ loading: boolean }>(withLenses({ loading: true }))
raises the following error:
Argument of type 'StateCreator<{ loading: true; }, [], []>' is not assignable to parameter of type 'StateCreator<{ loading: boolean; }, [], []>'.
Types of parameters 'getState' and 'getState' are incompatible.
Call signature return types '{ loading: boolean; }' and '{ loading: true; }' are incompatible.
The types of 'loading' are incompatible between these types.
Type 'boolean' is not assignable to type 'true'.
Type conflict with StateCreator
Since SetState
has been deprecated in zustand version 4.0.0, it is better to use StateCreator
instead, but there is a type conflict:
Argument of type 'readonly string[]' is not assignable to parameter of type '[]'.
The type 'readonly string[]' is 'readonly' and cannot be assigned to the mutable type '[]'.
Code:
interface IStore {
readonly app: IStoreApp;
}
type IStoreApp = ILanguageState;
const useStore = create<IStore>(
withLenses({
app: lens<IStoreApp, IStoreApp>((...accessors) => ({
...languageState(...accessors), // Error in `...accessors`
...initState?.app,
})),
})
);
export interface ILanguageState {
readonly language: string;
readonly setLanguage: (language: string) => void;
}
export const languageState: StateCreator<ILanguageState, [], [], ILanguageState> = (set) => ({
language: "en",
setLanguage: (language) =>
set(() => ({
language,
})),
});
And I do not know if this is bug or not, but in the generic type of the lens
function, you have to pass the state type 2 times, otherwise there will be the following error:
Argument of type 'StoreApi<object>' is not assignable to parameter of type 'StoreApi<ILanguageState>'.
The types returned by 'getState()' are incompatible between these types.
Type 'object' is not assignable to type 'ILanguageState'.
Source code:
export declare type Lens<T extends State, S extends State | StoreApi<State> = State, Setter extends SetState2<T> = SetState2<T>> = (set: Setter, get: GetState<T>, api: ResolveStoreApi<S>, path: ReadonlyArray<string>) => T;
And the problem seems to be that generic type of the api
argument is passed S
, instead of T
, and since (according to the documentation) we pass only 1 type, the api
will always be StoreApi<object>
, which causes conflicts.
Possible solution:
- export declare type Lens<T extends State, S extends State | StoreApi<State> = State, Setter extends SetState2<T> = SetState2<T>> = (set: Setter, get: GetState<T>, api: ResolveStoreApi<S>, path: ReadonlyArray<string>) => T;
+ export declare type Lens<T extends State, S extends State | StoreApi<State> = State, Setter extends SetState2<T> = SetState2<T>> = (set: Setter, get: GetState<T>, api: ResolveStoreApi<T>, path: ReadonlyArray<string>) => T;
Using with `devtools` middleware
Hi, I love your project as it allows me to do exactly what I need without too much type boilerplate. However, I have one question when using your library together with the devtools
middleware.
devtools
middleware extends the setter function, so you can supply the name of the action as a third argument to set
. But I can't seem to make that work together with this library. Do you have any ideas on how to do that? Thank you!
Example code:
interface UsersSlice {
byName: Map<string, User>
listUsers: () => void
}
const usersSlice: Lens<UsersSlice> = (set) => ({
byName: new Map(),
async listUsers() {
const data = await listUsersFromServer()
const byName = new Map(data.users.map((user) => [user.name, user]))
set({ byName }, false, '@users/listUsers')
},
})
export const useStore = create(
devtools(
immer(
withLenses(() => ({
users: lens(usersSlice),
}))
),
{ serialize: true }
)
)
Error when using global store with Typescript
I am trying to have a global store that can be retrieve inside lens for my store.
"@dhmk/zustand-lens": "^4.0.0",
"zustand": "^4.5.2"
Here's my code.
interface Store {
auth: AuthSlice;
sidebar: SidebarSlice;
loading: boolean;
}
export const useStore = create<Store>()(
devtools(
withLenses(() => ({
auth: lens<AuthSlice>(createAuthSlice),
sidebar: lens<SidebarSlice>(createSidebarSlice),
loading: false,
})),
)
);
So that in my slices, I can access the store by:
(set, get, store) => ({
// ...
store.setState({ loading: false });
})
There's no error popping up in my IDE, but get this error when starting react.
ERROR in src/stores/store.ts:15:16
TS2345: Argument of type '() => { auth: LensOpaqueType<AuthSlice, unknown>; sidebar: LensOpaqueType<SidebarSlice, unknown>; loading: boolean; }' is not assignable to parameter of type 'CheckLenses<{ auth: AuthSlice & LensTypeInfo<AuthSlice, StoreApi<unknown>>; sidebar: SidebarSlice & LensTypeInfo<SidebarSlice, StoreApi<...>>; loading: boolean; } & LensMeta<...>, WithDevtools<...>> | StateCreator<...>'.
Type '() => { auth: LensOpaqueType<AuthSlice, unknown>; sidebar: LensOpaqueType<SidebarSlice, unknown>; loading: boolean; }' is not assignable to type 'StateCreator<{ auth: AuthSlice & LensTypeInfo<AuthSlice, StoreApi<unknown>>; sidebar: SidebarSlice & LensTypeInfo<SidebarSlice, StoreApi<...>>; loading: boolean; }, [...], [], CheckLenses<...>>'.
Type '() => { auth: LensOpaqueType<AuthSlice, unknown>; sidebar: LensOpaqueType<SidebarSlice, unknown>; loading: boolean; }' is not assignable to type '(setState: <A extends string | { type: string; }>(partial: { auth: AuthSlice & LensTypeInfo<AuthSlice, StoreApi<unknown>>; sidebar: SidebarSlice & LensTypeInfo<...>; loading: boolean; } | Partial<...> | ((state: { ...; }) => { ...; } | Partial<...>), replace?: boolean, action?: A) => void, getState: () => { ...; }, ...'.
Type '{ auth: LensOpaqueType<AuthSlice, unknown>; sidebar: LensOpaqueType<SidebarSlice, unknown>; loading: boolean; }' is not assignable to type 'CheckLenses<{ auth: AuthSlice & LensTypeInfo<AuthSlice, StoreApi<unknown>>; sidebar: SidebarSlice & LensTypeInfo<SidebarSlice, StoreApi<...>>; loading: boolean; } & LensMeta<...>, WithDevtools<...>>'.
Property 'loading' is incompatible with index signature.
Type 'boolean' has no properties in common with type 'LensTypeInfo<unknown, StoreApi<unknown>>'.
13 | export const useStore = create<Store>()(
14 | devtools(
> 15 | withLenses(() => ({
| ^^^^^^^^
> 16 | auth: lens<AuthSlice>(createAuthSlice),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> 17 | sidebar: lens<SidebarSlice>(createSidebarSlice),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> 18 | loading: false,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> 19 | })),
| ^^^^^^^
20 | )
21 | );
22 |
Ts issue with lens method
import { create, StateCreator } from 'zustand'
import { devtools } from 'zustand/middleware'
import { withLenses, lens } from '@dhmk/zustand-lens'
export interface PayrollType {
balance: number
setBalance: (value: number) => void
}
export const createPayrollSlice: StateCreator<PayrollType> = (set) => ({
balance: 0,
setBalance: (value): void => set(() => ({ balance: value })),
})
interface FinanceStore {
payroll: PayrollType
}
export const useFinanceStore = create<FinanceStore>()(
devtools(
withLenses(() => ({
payroll: lens<PayrollType>((set, get, api) => createPayrollSlice(set, get, api)),
})),
{
name: 'FinanceStore',
enabled: process.env.NODE_ENV === 'development',
},
),
)
Error
type 'PayrollType' is not assignable to type 'CheckLenses<PayrollType, WithDevtools<StoreApi>> & LensMeta<CheckLenses<PayrollType, WithDevtools<StoreApi<...>>>, unknown>'.
Type 'PayrollType' is not assignable to type 'LensMeta<CheckLenses<PayrollType, WithDevtools<StoreApi>>, unknown>'.
Index signature for type 'string' is missing in type 'PayrollType'.ts(2322)
I tried manuall typing it and spent sometime it still failed
Setup store to be readonly
I'm loving this library. We're already using it, so it's going into production.
However, I noticed that due to an error, we used a sort function (which mutates arrays), causing the application to crash because Immer prohibits mutating the store.
We replaced the usage of .sort with .toSorted, resolving the issue since .toSorted is a non-mutating method. Nevertheless, we'd like TypeScript to assist us with this in the future.
How can we ensure that the store's type is readonly? I attempted to make all lenses ReadonlyDeep (using the type-fest utility), but that seems to result in a cascade of TypeScript issues.
Doesnt seem to work with persist middleware
Everything works fine when not using the persist middleware, however when the persist middleware is used the action function within the nested sub store becomes undefined and doesnt work anymore after page refresh
I have created a sandbox(with nextjs) that recreates the issue that I am talking about https://codesandbox.io/s/zustand-lens-persist-k0pxs5?file=/pages/index.tsx
Steps to reproduce the issue
- Open sandbox and on the first load, everything works as intended. clicking on the "change text" button simply appends "s" to the "text"
- Now refresh the codesandbox browser and you will notice that clicking on the button doesnt do anything(on my local I get the error "TypeError: changeText is not a function")
Any help is appreciated,
Thanks in advance
Expose the global state in the lenses
It would be nice if it were possible to access global state in the lenses. I.e., change the lens
signature from:
lens(fn: (set, get) => T): T
to
lens(fn: (set, get, getRoot) => T): T
or something like that.
possibility to call another slice action from inside a lens
From a slice X,Y,Z I need to call an action of a slice COMMON that has a shared method among all other slices.
I used to call it this way:
get().COMMON.setToast({ toast: UPDATE_ROWS_CATEGORY_SUCCESS })
once I wrap the slice with the lens() method get() only returns the type of the lensed slice i'm working on. Is it possibile to let get() access the whole store?
Unable to resolve peer dependency 'zustand@"^4.0.0"'
Context
Working on a React web app initialized with Vite, Trying out Zustand state management library. Needed a way to structure app state in a nested fashion
// for example
State
├ Authentication: AuthenticationSTate
├ UserSettings: UserSettingsState
├ RecentVisits: RecentVisitsState
├ etc...
After research this library seemed to be the solution for such an implementation, however when I tried installing it I kept getting the following error
npm WARN Could not resolve dependency:
npm WARN peer zustand@"^4.0.0" from @dhmk/[email protected]
I am currently using [email protected]
tried looking for a non-rc 4.0.0. version but with no luck.
After going through the code on zustand-4 branch "package.json" it looks like the version given for zustand doesn't exist.
Support use persist in lens object.
Sometimes, we may need partial state persistence.
How can zukeeper be added as middleware?
when add zukeeper as middleware like so:
import { create } from 'zustand'
import { immer } from 'zustand/middleware/immer'
import zukeeper from 'zukeeper'
import { withLenses } from '@dhmk/zustand-lens'
import { categories } from '../slices/categories'
import { projects } from '../slices/projects'
import { ui } from '../slices/ui'
import { IStore } from './types'
import { brandStyles } from '../slices/brandStyles'
import { listItemsInstance } from '../slices/listItemsInstance'
const useApplicationStore = create<IStore>()(
zukeeper(
immer(
withLenses(() => ({
ui,
categories,
brandStyles,
projects,
listItemsInstance,
})),
),
),
)
window.store = useApplicationStore
export { useApplicationStore }
I get this error:
main.3b53a5f….js:54795 Uncaught TypeError: Cannot set properties of undefined (setting 'setState')
at eval (immer.mjs:9:18)
at eval (index.js:6:19)
at createStoreImpl (vanilla.mjs:32:32)
at createStore (vanilla.mjs:35:52)
at createImpl (index.mjs:43:111)
at eval (index.ts:12:1)
at ./src/zustandStore/store/index.ts (main.3b53a5f….js:52692:1)
at __webpack_require__ (main.3b53a5f….js:54792:33)
at fn (main.3b53a5f….js:55045:21)
at eval (LoginModal.tsx:23:78)
TypeScript/Immer Support
Would you be able to provide an example of using this library with TypeScript and Immer please?
Immer TS example doesn't work
Hi. I'm using zustand4.0.0-1rc
and @dhmk/zustand-lens@next
https://codesandbox.io/s/confident-spence-0x2roj?file=/src/store.ts
zustand
changed types for StateCreator
and now code from immer example shows typescript errors
Using getters in state
In case anyone needs this, it is possible to add ES6 getters support.
Example:
const store = create(
withLenses(() => ({
sub: lens2((set, get) => ({
items: [1, 2],
get count() {
return get().items.length;
},
add(x) {
set({ items: get().items.concat(x) });
},
})),
}))
);
There are caveats, however:
- It may degrade performance. Here's a benchmark. It is especially daunting in Firefox.
- It won't work with Immer.
- It won't work with nested lenses (can be solved).
See https://github.com/dhmk083/dhmk-zustand-lens/tree/getters for more.
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.