zustand

698 阅读2分钟

中文音译: "su s dan t"
一个小型、快速且可扩展的 bearbones 状态管理解决方案。 Zustand 有一个基于钩子的舒适 API。它不是样板式的或固执己见的,但有足够的约定来明确和类似通量。
不要忽视它,因为它很可爱,它有爪子!我们花了很多时间来处理常见的陷阱,比如可怕的僵尸子问题、React 并发以及混合渲染器之间的上下文丢失。它可能是 React 空间中的唯一状态管理器,可以正确处理所有这些问题。

API

usage

docs.pmnd.rs/zustand/rec…

原生环境

  • 使用 createStore 创建store 对象
  • 使用 useStore 创建在react 中可以使用hooks
import { useStore } from 'zustand';
import { createStore } from 'zustand/vanilla';

interface IState {
  bears: number;
  increase: (by: number) => void;
}

const vanillaStore = createStore<IState>((set) => ({
  bears: 0,
  increase: (by) => set((state) => ({ bears: state.bears + by })),
}));

const useStoreHook = () => useStore(vanillaStore);

export default function VanillaStoreDemo() {
  const state = useStoreHook();
  return (
    <section>
      <button
        onClick={() => {
          state.increase(1);
        }}
        >
        click
      </button>
      <div>{state.bears}</div>
    </section>
  );
}

react环境

  • 调用 create创建 hooks useStore
  • 直接在react环境中使用
import { create } from 'zustand';

const useStore = create<{ counter: number; inc: any }>((set) => ({
    counter: 1,
    inc: () => set((pre) => ({ ...pre, counter: pre.counter + 1 })),
}));

export default function ZustandDemo() {
    const store = useStore((state) => state);
    return (
        <section>
            <button
                onClick={() => {
                    store.inc();
                }}
            >
                click
            </button>
            <div>{store.counter}</div>
        </section>
    );
}

结合中间件

类redux的调用

import { create } from 'zustand';
import { redux } from 'zustand/middleware';

const types = { increase: 'INCREASE', decrease: 'DECREASE' };

const reducer = (state: any, { type, by = 1 }: any) => {
    switch (type) {
        case types.increase:
            return { grumpiness: state.grumpiness + by };
        case types.decrease:
            return { grumpiness: state.grumpiness - by };
        default: {
            return state;
        }
    }
};

const useReduxStore = create(redux(reducer, { grumpiness: 0 }));

export default function MiddlewareRedux() {
    const { dispatch, ...state } = useReduxStore();
    return (
        <section>
            <button
                onClick={() => {
                    dispatch({ type: types.increase });
                }}
            >
                increase
            </button>
            <button
                onClick={() => {
                    dispatch({ type: types.decrease });
                }}
            >
                decrease
            </button>
            <div>{state.grumpiness}</div>
        </section>
    );
}

结合immer

// 当produce中传入一个函数的时候,会返回一个函数用来接受baseState
import produce from 'immer'
const immerUpdater = produce((draft) => {
  if (draft.value > 3) {
    draft.status = '↑';
    // return { name: '↑' };
  } else if (draft.value === 3) {
    draft.status = '--';
    // return { name: '--' };
  } else {
    // 会被重置为一个新的状态
    return { name: '↓' };
  }
});

console.log(immerUpdater({ value: 5 })); // {value: 5, status: '↑'}
console.log(immerUpdater({ value: 3 })); // {value: 3, status: '--'}
console.log(immerUpdater({ value: 1 })); // {name: '↓'}
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';

interface Todo {
    id: string;
    title: string;
    done: boolean;
}

type State = {
    todos: Array<Todo>;
};

type Actions = {
    toggleTodo: (todoId: string) => void;
};

export const useTodoStore = create(
    immer<State & Actions>((set) => ({
        todos: [
            {
                id: '82471c5f-4207-4b1d-abcb-b98547e01a3e',
                title: 'Learn Zustand',
                done: false,
            },
            {
                id: '354ee16c-bfdd-44d3-afa9-e93679bda367',
                title: 'Learn Jotai',
                done: false,
            },
            {
                id: '771c85c5-46ea-4a11-8fed-36cc2c7be344',
                title: 'Learn Valtio',
                done: false,
            },
            {
                id: '363a4bac-083f-47f7-a0a2-aeeee153a99c',
                title: 'Learn Signals',
                done: false,
            },
        ],
        toggleTodo: (todoId: string) => {
            set((state) => {
                const todo = state.todos.find((d) => d.id === todoId);
                if (todo) {
                    todo.done = !todo.done;
                }
            });
        },
    })),
);

export default function MiddlewareImmer() {
    const { todos, toggleTodo } = useTodoStore();

    return (
        <section>
            <ul>
                {todos.map((todo) => {
                    return (
                        <li
                            key={todo.id}
                            style={{
                                textDecorationLine: todo.done ? 'line-through' : 'none',
                                width: 200,
                                display: 'flex',
                                justifyContent: 'space-between',
                                marginTop: 10,
                            }}
                        >
                            <span>{todo.title}</span>
                            <button onClick={() => toggleTodo(todo.id)}>toggle</button>
                        </li>
                    );
                })}
            </ul>
        </section>
    );
}

其他

docs.pmnd.rs/zustand/int…

基于proxy的valtio

valtio.pmnd.rs/