zustand-utils

some utils for zustand


Keywords
zustand
License
MIT
Install
npm install zustand-utils@1.1.2

Documentation

English | 简体中文

zustand-utils

NPM version NPM downloads install size

Test CI status Deploy CI Coverage

 docs by dumi Build With father

Introduction

Some utils for zustand

createContext

A replacement createContext from zustand/context that is deprecated in v4 and will be removed in v5. (Discussion: #1276)

import create from 'zustand'
import { createContext } from 'zustand-utils'

const { Provider, useStore } = createContext()

const createStore = () => create(...)

const App = () => (
  <Provider createStore={createStore}>
    ...
  </Provider>
)

const Component = () => {
  const state = useStore()
  const slice = useStore(selector)
  ...

optionalDevtools

a wrapper for zustand devtools middleware that with config to enable devtools.(Discussion: #1266)

import { optionalDevtools } from 'zustand-utils';

type Store = {
  foo: string;
};

export const createStore = (withDevtools?: boolean) => {
  // can enable or controlled by config
  const devtools = optionalDevtools(withDevtools);
  // use as zustands/middleware devtools
  return create<Store>()(devtools((set) => ({})));
};

What's improve?

zustands/middleware's devtools have an option enable, but it doesn't work if set to false in development.

Usage

createContext

createContext usage in real components

Migration from zustand-v3-create-context.md

import create from "zustand";
import { createContext } from 'zustand-utils';

// Best practice: You can move the below createContext() and createStore to a separate file(store.js) and import the Provider, useStore here/wherever you need.

const { Provider, useStore } = createContext();

const createStore = () =>
  create((set) => ({
    bears: 0,
    increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
    removeAllBears: () => set({ bears: 0 })
  }));

const Button = () => {
  return (
      {/** store() - This will create a store for each time using the Button component instead of using one store for all components **/}
    <Provider createStore={createStore}>
      <ButtonChild />
    </Provider>
  );
};

const ButtonChild = () => {
  const state = useStore();
  return (
    <div>
      {state.bears}
      <button
        onClick={() => {
          state.increasePopulation();
        }}
      >
        +
      </button>
    </div>
  );
};

export default function App() {
  return (
    <div className="App">
      <Button />
      <Button />
    </div>
  );
}

createContext usage with initialization from props

Migration from zustand-v3-create-context.md

import create from 'zustand';
import { createContext } from 'zustand-utils';

const { Provider, useStore } = createContext();

export default function App({ initialBears }) {
  return (
    <Provider
      createStore={() =>
        create((set) => ({
          bears: initialBears,
          increase: () => set((state) => ({ bears: state.bears + 1 })),
        }))
      }
    >
      <Button />
    </Provider>
  );
}

Refactor app store to a component store with createContext

a most usage of createContext is refactoring an app to a component. Here's progress:

  1. Create an App without context :
// store.ts
import create from 'zustand';

export const useStore = create((set) => ({
  bears: 0,
  increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
  removeAllBears: () => set({ bears: 0 }),
}));

components in app use useStore to consume store:

// Component.ts

import { useStore } from './store';

const ButtonChild = () => {
  const state = useStore();
  return (
    <div>
      {state.bears}
      <button
        onClick={() => {
          state.increasePopulation();
        }}
      >
        +
      </button>
    </div>
  );
};
export default ButtonChild;
  1. Just wrapper the App with createContext, and don't need to refactor any code in children components.
// store.ts
import create from "zustand";

+ const createStore = ()=> create((set) => ({
- export const useStore =  create((set) => ({
    bears: 0,
    increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
    removeAllBears: () => set({ bears: 0 })
  }));

+ const { Provider, useStore } = createContext();

+ export { Provider, useStore ,  createStore }
// Wrapper.tsx

import { createStore, Provider } from './store';

const Wrapper = () => {
  return (
    <Provider createStore={createStore}>
      <ButtonChild />
    </Provider>
  );
};

It become a component, can be used in any other app.

createStoreUpdater

createStoreUpdater is a function for updating the value of a specified key in the Store.

Parameters

createStoreUpdater takes a StoreApi object as its parameter, which contains methods for manipulating the Store, such as getState, setState, subscribe, and destroy.

createStoreUpdater returns a function that takes the following parameters:

  • key: the key in the Store that needs to be updated;
  • value: the value that needs to be updated;
  • deps: an array of dependencies, defaults to [value];
  • setStoreState: an optional callback function for updating the Store state, defaults to storeApi.setState.

Return Value

createStoreUpdater returns a function that updates the value of the specified key in the Store.

Example

import { createStoreUpdater } from 'path/to/createStoreUpdater';
import { useStore } from 'path/to/useStore';

interface User {
  name: string;
  age: number;
}

const storeApi = useStore<User>({ name: '', age: 0 });
const updateUser = createStoreUpdater(storeApi);

// Update name
updateUser('name', 'John Doe');

// Update age
updateUser('age', 18);

In the example above, we first create a Store using useStore, then create an updater updateUser using createStoreUpdater, and finally update the name and age in the Store by calling updateUser.

License

MIT