Skip to content

Commit

Permalink
update readme
Browse files Browse the repository at this point in the history
  • Loading branch information
Arabinda committed Dec 26, 2024
1 parent dda7924 commit e967e0d
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 69 deletions.
71 changes: 30 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
> [!NOTE]
> `@udecode/zustood` has been renamed to `zustand-x`.
> [!NOTE] > `@udecode/zustood` has been renamed to `zustand-x`.
> Using Jotai? See [JotaiX](https://github.com/udecode/jotai-x).
# ZustandX
Expand All @@ -21,12 +20,9 @@ code.
which solves these challenges, so you can focus on your app.

```bash
yarn add zustand@4.4.7 zustand-x
yarn add zustand@latest zustand-x
```

> [!IMPORTANT]
> `zustand` 4.5.0+ is not yet supported. See https://github.com/udecode/zustand-x/issues/79.
Visit [zustand-x.udecode.dev](https://zustand-x.udecode.dev) for the
API.

Expand All @@ -43,16 +39,16 @@ API.
## Create a store

```ts
import { createStore } from 'zustand-x'
import { createStore } from 'zustand-x';

const repoStore = createStore('repo')({
const repoStore = createStore('repo')(() => ({
name: 'zustandX',
stars: 0,
owner: {
name: 'someone',
email: 'someone@xxx.com',
},
})
}));
```

- the parameter of the first function is the name of the store, this is
Expand All @@ -66,10 +62,10 @@ Note that the zustand store is accessible through:

```ts
// hook store
repoStore.useStore
repoStore.useStore;

// vanilla store
repoStore.store
repoStore.store;
```

## Selectors
Expand All @@ -80,8 +76,8 @@ Use the hooks in React components, no providers needed. Select your
state and the component will re-render on changes. Use the `use` method:

```ts
repoStore.use.name()
repoStore.use.stars()
repoStore.use.name();
repoStore.use.stars();
```

We recommend using the global hooks (see below) to support ESLint hook
Expand All @@ -95,7 +91,7 @@ Use the tracked hooks in React components, no providers needed. Select your
state and the component will trigger re-renders only if the **accessed property** is changed. Use the `useTracked` method:

```ts
repoStore.useTracked.owner()
repoStore.useTracked.owner();
```

### Getters
Expand All @@ -104,14 +100,14 @@ Don't overuse hooks. If you don't need to subscribe to the state, use
instead the `get` method:

```ts
repoStore.get.name()
repoStore.get.stars()
repoStore.get.name();
repoStore.get.stars();
```

You can also get the whole state:

```ts
repoStore.get.state()
repoStore.get.state();
```

### Extend selectors
Expand All @@ -124,7 +120,7 @@ full typescript support:
const repoStore = createStore('repo')({
name: 'zustandX',
stars: 0,
middlewares: ['immer', 'devtools', 'persist']
middlewares: ['immer', 'devtools', 'persist'],
})
.extendSelectors((state, get, api) => ({
validName: () => get.name().trim(),
Expand All @@ -134,17 +130,17 @@ const repoStore = createStore('repo')({
// get.validName is accessible
title: (prefix: string) =>
`${prefix + get.validName()} with ${get.stars()} stars`,
}))
// extend again...
}));
// extend again...
```

## Actions

Update your store from anywhere by using the `set` method:

```ts
repoStore.set.name('new name')
repoStore.set.stars(repoStore.get.stars + 1)
repoStore.set.name('new name');
repoStore.set.stars(repoStore.get.stars + 1);
```

### Extend actions
Expand Down Expand Up @@ -178,8 +174,8 @@ const repoStore = createStore('repo')({
set.validName(name);
set.stars(0);
},
}))
// extend again...
}));
// extend again...
```

## Global store
Expand Down Expand Up @@ -218,14 +214,8 @@ export const actions = mapValuesKey('set', rootStore);
### Global hook selectors

```ts
import shallow from 'zustand/shallow'

useStore().repo.name()
useStore().modal.isOpen()

// prevent unnecessary re-renders
// more see: https://docs.pmnd.rs/zustand/recipes#selecting-multiple-state-slices
useStore().repo.middlewares(shallow)
useStore().repo.name();
useStore().modal.isOpen();
```

### Global tracked hook selectors
Expand Down Expand Up @@ -258,17 +248,17 @@ By using `useStore() or useTrackStore()`, ESLint will correctly lint hook errors
### Global getter selectors

```ts
store.repo.name()
store.modal.isOpen()
store.repo.name();
store.modal.isOpen();
```

These can be used anywhere.

### Global actions

```ts
actions.repo.stars(store.repo.stars + 1)
actions.modal.open()
actions.repo.stars(store.repo.stars + 1);
actions.modal.open();
```

These can be used anywhere.
Expand All @@ -279,7 +269,6 @@ The second parameter of `createStore` is for options:

```ts
export interface CreateStoreOptions<T extends State> {
middlewares?: any[];
devtools?: DevtoolsOptions;
immer?: ImmerOptions;
persist?: PersistOptions;
Expand All @@ -289,13 +278,13 @@ export interface CreateStoreOptions<T extends State> {
### Middlewares

ZustandX is using these middlewares:
- `immer`: required. Autofreeze can be enabled using
`immer.enabledAutoFreeze` option.
- `devtools`: enabled if `devtools.enabled` option is `true`.

- `immer`: enabled if `immer.enabled` option is `true`. `immer` implements from [zustand](https://github.com/pmndrs/zustand?tab=readme-ov-file#immer-middleware).
- `devtools`: enabled if `devtools.enabled` option is `true`. `devtools` implements `DevtoolsOptions` interface from [zustand](https://github.com/pmndrs/zustand?tab=readme-ov-file#redux-devtools).
- `persist`: enabled if `persist.enabled` option is `true`. `persist`
implements `PersistOptions` interface from
[zustand](https://github.com/pmndrs/zustand#persist-middleware)
- custom middlewares can be added using `middlewares` option
- custom middlewares can be added by wrapping state initiator. [check here](https://zustand.docs.pmnd.rs/middlewares/combine)

## Contributing and project organization

Expand Down
49 changes: 22 additions & 27 deletions packages/zustand-x/src/createStore.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { createTrackedSelector } from 'react-tracked';
import { createWithEqualityFn as createStoreZustand } from 'zustand/traditional';

import {
devtools as devToolsMiddleware,
devToolsMiddleware,
DevtoolsOptions,
persist as persistMiddleware,
immerMiddleware,
ImmerOptions,
persistMiddleware,
PersistOptions,
} from 'zustand/middleware';
import { immer as immerMiddleware } from 'zustand/middleware/immer';
import { createWithEqualityFn as createStoreZustand } from 'zustand/traditional';

} from './middlewares';
import { TStateApi, TStoreInitiatorType } from './types';
import { generateStateActions } from './utils/generateStateActions';
import { generateStateGetSelectors } from './utils/generateStateGetSelectors';
Expand All @@ -18,29 +19,23 @@ import { storeFactory } from './utils/storeFactory';
import type { StoreMutatorIdentifier } from 'zustand';

type TCreateStoreOptions<StateType> = {
persist?: Partial<PersistOptions<StateType>> & {
enabled?: boolean;
};
devtools?: Partial<DevtoolsOptions> & {
enabled?: boolean;
};
immer?: {
enabled?: boolean;
};
persist?: PersistOptions<StateType>;
devtools?: DevtoolsOptions;
immer?: ImmerOptions;
};

type DefaultMutators<
StateType,
Options extends TCreateStoreOptions<StateType>,
> = [
...(Options['devtools'] extends { enabled: true }
? [['zustand/devtools', never]]
...(Options['immer'] extends { enabled: true }
? [['zustand/immer', never]]
: []),
...(Options['persist'] extends { enabled: true }
? [['zustand/persist', StateType]]
: []),
...(Options['immer'] extends { enabled: true }
? [['zustand/immer', never]]
...(Options['devtools'] extends { enabled: true }
? [['zustand/devtools', never]]
: []),
];

Expand Down Expand Up @@ -73,11 +68,9 @@ export const createStore =
initializer: TStoreInitiatorType<StateType, any, any, any>
) => TStoreInitiatorType<StateType, any, any, any>)[] = [];

//enable devtools
if (devtools && devtools.enabled) {
middlewares.push((config) =>
devToolsMiddleware(config, { ...devtools, name: devtools.name ?? name })
);
//enable immer
if (immer && immer.enabled) {
middlewares.push(immerMiddleware);
}

//enable persist
Expand All @@ -90,9 +83,11 @@ export const createStore =
);
}

//enable immer
if (immer && immer.enabled) {
middlewares.push(immerMiddleware);
//enable devtools
if (devtools && devtools.enabled) {
middlewares.push((config) =>
devToolsMiddleware(config, { ...devtools, name: devtools.name ?? name })
);
}

const stateMutators = middlewares.reduce(
Expand Down
7 changes: 7 additions & 0 deletions packages/zustand-x/src/middlewares/devtools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { DevtoolsOptions as _DevtoolsOptions } from 'zustand/middleware';

import { MiddlewareOption } from '../types';

export { devtools as devToolsMiddleware } from 'zustand/middleware';

export type DevtoolsOptions = MiddlewareOption<Partial<_DevtoolsOptions>>;
4 changes: 4 additions & 0 deletions packages/zustand-x/src/middlewares/immer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { MiddlewareOption } from '../types';

export { immer as immerMiddleware } from 'zustand/middleware/immer';
export type ImmerOptions = MiddlewareOption<{}>;
3 changes: 3 additions & 0 deletions packages/zustand-x/src/middlewares/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './devtools';
export * from './immer';
export * from './persist';
8 changes: 8 additions & 0 deletions packages/zustand-x/src/middlewares/persist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { PersistOptions as _PersistOptions } from 'zustand/middleware';

import { MiddlewareOption } from '../types';

export { persist as persistMiddleware } from 'zustand/middleware';
export type PersistOptions<StateType> = MiddlewareOption<
Partial<_PersistOptions<StateType>>
>;
4 changes: 4 additions & 0 deletions packages/zustand-x/src/types/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,7 @@ export type TCreatedStoreType<
StateType,
Mutators extends [StoreMutatorIdentifier, unknown][],
> = UseBoundStoreWithEqualityFn<Mutate<StoreApi<StateType>, Mutators>>;

export type MiddlewareOption<T> = T & {
enabled?: boolean;
};
4 changes: 3 additions & 1 deletion packages/zustand-x/src/utils/mapValuesKey.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import mapValues from 'lodash.mapvalues';

import { TStateApi } from '../types';

export const mapValuesKey = <
VK extends string,
VK extends keyof TStateApi<any, any, any, any>,
T extends Record<VK, any>,
R extends Record<keyof R, T>,
>(
Expand Down

0 comments on commit e967e0d

Please sign in to comment.