🌟What is zoov

Zoov is a State management solution build on top of zustand. So I called it: Zoov = Zustand + Module

Compared to zustand, zoov improves it's capability in these fields:

TypeScript first

Zoov was built for TypeScript users. Type inference are put in the first place in all it's API's design. You can expect a good Developing Experience in using zoov.

For Example:

const module = defineModule({ count: 0 })
    // The `state` here will have type { count: number }
    .actions({ add: (state, amount: number)=> ... })
    // The `getActions` here will have type { add(amount: number): void }
    .methods(({ getActions }) => ({}))
    .build()

Mutable state

Though Immutable state is always recommended in React world. In my opinion, mutable state is usually easier and cleaner. So, I decided to make state mutable with the help of immer.

In zoov, mutable state is restricted in a smaller scope (Only in module actions), it will not affact the main concept of React.

module.actions({
  addTodo: (state, title: string) => {
    state.todos.push({ title, checked: false });
  },
});

Incremental API

Zustand is simple, and zoov doesn't intend to complicate your code base. From my perspective, zoov is even simpler and easier to use.

For example, if you want to create a global store to share a count value. One line of js code is enough

const module = defineModule({ count: 0 }).build()

To use it in component

const [{ count }, { $setState }] = module.use()

// $setState is the internal action of a module
const add = $setState("count", v => v + 1)

In zoov, all the features are optional and can be added on demand. For example:

  • If you found that a state mutation is shared among different components, you can define a module action/method to reduce code duplication.

  • If you want to use async action or even take advantage of rxjs, you can put these code in module methods.

  • If you want to persist the store or debug the store with redux-devtools, you can easily do it leveraging the zustand middleware

Context & Scoped

Zoov designed it's context API with the concept of Algebraic Effect, if you have not heard of this, you can read this post.

Most cases, you don't need a scope context, each module has a global scope by default. But if you want to modify the default state, or compose a custom middleware, you can define a provider to create a local context.

You don't need to worry about the performance issue, the scope will not be built until you use it in.

The Context is composable, the child context will inhrerit evertying from the parent context. You can read more info from Context & Scope

const Provider= defineProvider((handle) => {
  handle(CounterModule, {
    defaultValue: { count: 1 },
    middleware: (store) => persist(store, { name: 'count2' }),
  });
  
   handle(AnotherModule, {
    defaultValue: { ... },
  });
});

Flexible

Zoov is a React hook library, but it's state is stored outside React, so it's possible to get/set the module state in any function.

This is extremely useful when you want to want to define a static function to mutate the module state, or get the latest value in a callback function.

function add() {
  module.getActions().$setState("count", v => v + 1)
}

useEffect(() => {
  const timer = setTimeout(() => {
    // Get the latest module state
    console.log(module.getState());
    // Mutate the store via a static function
    add()
  }, 1000);
  return () => clearTimeout(timer);
}, []);

Sometimes, a module state is handled by a provider. But it's also possible to attach to the module scope , with useScopeContext

const context = useScopeContext();
const actions= module.getActions(context);

Last updated