# @sinakhx/use-zustand-store

> helpers for using zustand as a local store in react apps

Latest version **0.3.0** (published 2022-07-19) · MIT license · 0 weekly downloads

## Install

```sh
npm install @sinakhx/use-zustand-store
pnpm add @sinakhx/use-zustand-store
yarn add @sinakhx/use-zustand-store
bun add @sinakhx/use-zustand-store
```

## Health

**Score 30/100 (F)** — status: abandoned.

Positive: has types; esm support; no vulnerabilities; high quality score.

Warnings: low downloads; pre 1.0.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 0.3.0 |
| Published | 2022-07-19 |
| First published | 2022-06-04 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 4 |
| Unpacked size | 16.4 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 2 |
| Author | Sina Khodabandehloo |
| Maintainers | sinakhx |
| Keywords | zustand, react, local, store, state, hooks |

## Links

- npm: https://www.npmjs.com/package/@sinakhx/use-zustand-store
- Repository: https://github.com/Sinakhx/use-zustand-store
- Homepage: https://github.com/Sinakhx/use-zustand-store#readme
- Issues: https://github.com/Sinakhx/use-zustand-store/issues
- npm.io page: https://npm.io/package/@sinakhx/use-zustand-store

## Dependencies (4)

- [react](https://npm.io/package/react.md) ^16.0.0 || ^17.0.0 || ^18.0.0
- [zustand](https://npm.io/package/zustand.md) ^4.0.0-rc.1
- [optics-ts](https://npm.io/package/optics-ts.md) ^2.3.0
- [react-tracked](https://npm.io/package/react-tracked.md) ^1.7.9

## Alternatives

- [@reckona/mreact-store](https://npm.io/package/@reckona/mreact-store.md) — 976 weekly downloads
- [regular-state](https://npm.io/package/regular-state.md) — 410 weekly downloads
- [@pacote/flux-actions](https://npm.io/package/@pacote/flux-actions.md) — 65 weekly downloads
- [@pilotlab/lux-debug](https://npm.io/package/@pilotlab/lux-debug.md) — 39 weekly downloads
- [vue-persist-state](https://npm.io/package/vue-persist-state.md) — 19 weekly downloads

## Recent versions

- 0.3.0 (latest) — 2022-07-19
- 0.2.0 — 2022-06-09
- 0.1.4 — 2022-06-06
- 0.1.0 — 2022-06-05
- 0.0.5 — 2022-06-04
- 0.0.4 — 2022-06-04
- 0.0.3 — 2022-06-04

## README

# **@sinakhx/useZustandStore**
![npm](https://img.shields.io/npm/v/@sinakhx/use-zustand-store?color=%23b8860b&style=flat-square)
![license](https://img.shields.io/npm/l/@sinakhx/use-zustand-store?color=red&style=flat-square)
![types](https://img.shields.io/npm/types/@sinakhx/use-zustand-store?style=flat-square)

custom helpers for using [zustand](https://github.com/pmndrs/zustand) in react apps.
it can be used for creating local (component scoped) stores using Zustand. So that:
- you won't need to worry about garbage collecting your store on page components' unmount lifecycle.
- you can get rid of using multiple selectors to acces different parts of the store (as it's using [react-tracked](https://github.com/dai-shi/react-tracked) under the hood)
- you avoid making your codebase weird with currying, Providers, mind-boggling type annotations, etc.

## Installation
```bash
npm install @sinakhx/use-zustand-store
```

## Usage

Creating a store is exactly the same way as creating a store in Zustand. You only need to change Zustand's `create` function with this library's `createZustandStore` function. Everything else is the same. (It's just a wrapper to avoid nesting due to currying)

**Example counter app:**

*counterStore.ts*
```ts
import { createZustandStore, mutateStoreItem } from '@sinakhx/use-zustand-store'

interface ICounterStore {
    count: number
    increment: () => void
}

export const counterStore = createZustandStore<ICounterStore>((set) => ({
    count: 0,
    increment: () => set((state) => ({ count: state.count + 1 })),
}))

```

*CounterComponent.tsx*
```tsx
import { useZustandStore } from '@sinakhx/use-zustand-store'
import { counterStore } from './counterStore'

const CounterComponent = () => {
    const store = useZustandStore(counterStore)
    return <button onClick={store.increment}>{store.count}</button>
}

export default CounterComponent
```

Now the store is bound to the component. By changing the page route (unmounting the component), the store gets garbage collected & by going back to the page (mounting the component again), a fresh store is created.

That's done! Happy coding!

<details>
<summary style="font-weight:bold;">Simpler store mutations</summary>

Instead of using Immer or nested destructuring to mutate the store, you can use the `mutateStoreItem` helper.

The following example demonstrates how to reduce multiple `useState` hooks to a single store. 

*tableStore.ts*
```ts
import { createZustandStore, mutateStoreItem } from '@sinakhx/use-zustand-store'

type TableRow = {
    id: number
    name: string
    age: number
}

interface ITableStore {
    rows: Array<TableRow>
    setRows: (rows: TableRow[]) => void
    selectedRow: TableRow | null
    setSelectedRow: (row: TableRow | null) => void
    handleDeleteRow: (id: number) => void
}

const counterStore = createZustandStore<ITableStore>((set, get) => ({
    rows: [],
    setRows: (rows) => set(mutateStoreItem({ rows })),
    selectedRow: null,
    setSelectedRow: (row) => set(mutateStoreItem({ selectedRow: row })),
    handleDeleteRow: (id) => {
        const newRows = get().rows.filter((row) => row.id !== id)
        get().setRows(newRows)
    },
}))
```

`mutateStoreItem` is using [optics-ts](https://github.com/akheron/optics-ts) to access the store's state. As a result one can also easily mutate a nested store item by providing its path as object key. e.g: `set(mutateStoreItem({ 'user.info.name': 'John' }))`.
</details>

<details>
<summary style="font-weight:bold;">Advanced usage: initializing store with props</summary>

*counterStore.ts*
```ts
import { createZustandStore } from '@sinakhx/use-zustand-store'

interface ICounterStore {
    count: number
    increment: () => void
}

interface ICounterProps {
    initialCount: number
}

export const counterStoreFactory = ({ initialCount } : ICounterProps) => createZustandStore<ICounterStore>((set) => ({
    count: initialCount,
    increment: () => set((state) => ({ count: state.count + 1 })),
}))

```

*CounterComponent.tsx*
```tsx
import { useZustandStore } from '@sinakhx/use-zustand-store'
import { counterStoreFactory } from './counterStore'

interface ICounterProps {
    initialCount: number
}

const CounterComponent = ({ initialCount }: ICounterProps) => {
    const store = useZustandStore(counterStoreFactory({ initialCount }))
    return <button onClick={store.increment}>{store.count}</button>
}

export default CounterComponent
```

</details>

<details>
<summary style="font-weight:bold;">Still need global stores in other scenarios? no problem!</summary>

In that case, you can create a global version of the `useZustandStore` hook by using the `createTrackedSelector` helper from [react-tracked](https://github.com/dai-shi/react-tracked)

*counterStore.ts*
```ts
import { createZustandStore, createTrackedSelector } from '@sinakhx/use-zustand-store'

interface ICounterStore {
    count: number
    increment: () => void
}

const counterStore = createZustandStore<ICounterStore>((set) => ({
    count: 0,
    increment: () => set((state) => ({ count: state.count + 1 })),
}))

export const useGlobalCounterStore = createTrackedSelector(counterStore())

```

*CounterComponent.tsx*
```tsx
// import { useZustandStore } from '@sinakhx/use-zustand-store'
import { useGlobalCounterStore } from './counterStore'

const CounterComponent = () => {
    const store = useGlobalCounterStore()
    return <button onClick={store.increment}>{store.count}</button>
}

export default CounterComponent
```

now the store is independent from the components & will keep its state regardless of the route changes.
</details>

<!--
____________________________________
### **Want More Examples?**
see the [tests folder][tests-url] for more detailed examples.
-->
____________________________________
### **Contributing**
Please feel free to open an issue or create a pull request to add a new feature or fix a bug. (see [contributing][contribution-url] for more details)

____________________________________

## **License**

The [MIT License][license-url] (MIT)

&copy; 2022 Sina Khodabandehloo

[tests-url]: https://github.com/Sinakhx/use-zustand-store/tree/main/__tests__/
[contribution-url]:  https://github.com/Sinakhx/use-zustand-store/blob/main/CONTRIBUTING.md
[changelog-url]:  https://github.com/Sinakhx/use-zustand-store/blob/main/CHANGELOG.md
[license-url]:  https://github.com/Sinakhx/use-zustand-store/blob/main/LICENSE

---
_Source: https://npm.io/package/@sinakhx/use-zustand-store · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
