zoo

zoov

Use ๐Ÿป Zustand with Module-like api

Showing:

Popularity

Downloads/wk

6

GitHub Stars

16

Maintenance

Last Commit

1mo ago

Contributors

1

Package

Dependencies

0

License

MIT

Type Definitions

Built-In

Tree-Shakeable

Yes?

Categories

Readme

ZOOV

โœจ ZOOV = Zustand + Module

Build Status Code Coverage npm-v npm-d brotli

Features

  • ๐Ÿ˜Œ Comfortable type inference
  • โœจ Immer in the first class support
  • ๐Ÿณ 150 line code based on Zustand
  • ๐Ÿงฎ Modular state management (Redux-like)
  • ๐Ÿ“– Scope supported with Algebraic Effects

Quick Start

You can try this Example

Or install locally

yarn add immer zustand # peer dependencies
yarn add zoov

First Glance

const CounterModule = defineModule({ count: 0 })
  .actions({
    add: (draft) => draft.count++,
    minus: (draft) => draft.count--,
  })
  .computed({
    doubled: (state) => state.count * 2,
  })
  .build();

const App = () => {
  const [{ count }, { add }] = CounterModule.use();
  return <button onClick={add}>{count}</button>;
};

// state is shared
const App2 = () => {
  const { doubled } = CounterModule.useComputed();
  return <div>doubled: {doubled}</div>;
};

More Examples

Use Methods

const CounterModule = defineModule({ count: 0 })
  .actions({
    add: (draft) => draft.count++,
    minus: (draft) => draft.count--,
  })
  .computed({
    doubled: (state) => state.count * 2,
  })
  .methods((perform, effect) => {
    const { add, minus } = perform.getActions();
    return {
      addAndMinus: () => {
        add();
        add();
        setTimeout(() => minus(), 100);
      },
      // async function is supported
      asyncAdd: async () => {
        await something()
        add()
      },
      // !rxjs is required if you want to use effect
      addAfter: effect<number>((payload$) =>
        payload$.pipe(
          exhaustMap((timeout) => {
            return timer(timeout).pipe(tap(() => add()));
          })
        )
      ),
    };
  })
  .build();

Use Selector

const CounterModule = defineModule({ count: 0, input: 'hello' })
  .actions({
    add: (draft) => draft.count++,
    setInput: (draft, value: string) => (draft.input = value),
  })
  .build();

const App = () => {
  // <App /> will not rerender unless "count" changes
  const [count] = CounterModule.use((state) => state.count);
  return <span>{count}</span>;
};

Use Middleware

// see more examples in https://github.com/pmndrs/zustand/blob/master/src/middleware.ts
const Module = defineModule({ count: 0 })
  .actions({ add: (draft) => draft.count++ })
  .middleware((store) => persist(store, { name: 'counter' }))
  .build();

Use Provider

import { defineProvider } from 'zoov';

const CustomProvider = defineProvider((handle) => {
  // create a new Module scope for all its children(can be nested)
  handle(YourModule, {
    defaultState: {},
  });
  handle(AnotherModule, {
    defaultState: {},
  });
});

const App = () => {
  // if a Module is not handled by any of its parent, then used global scope
  return (
    <div>
      <CustomProvider>
        <Component />
      </CustomProvider>
      <Component />
    </div>
  );
};

Rate & Review

Great Documentation0
Easy to Use0
Performant0
Highly Customizable0
Bleeding Edge0
Responsive Maintainers0
Poor Documentation0
Hard to Use0
Slow0
Buggy0
Abandoned0
Unwelcoming Community0
100