Next up: My ideal state management tool

As MENTIONED earlier, for me, the ideal state management tool only needs two things:

  • Easy to use and suitable for medium to large projects
  • Supports Typescript perfectly

I couldn’t find one that was perfect for both, so I decided to build my own: Stamen.

Firstly, it is easy to use and suitable for medium and large projects. The Api design of Stamen borrowed from DVA, Mirror and Rematch, but it is simpler and mainly borrowed from the organization mode of their models: State, reducers and Effects. It is good practice to divide actions into reducer and effect categories.

Take a look at Stamen in action:

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'stamen';

const CounterStore = createStore({
  state: {
    count: 10,},reducers: { increment(state) { state.count++; }, decrement(state) { state.count--; }},effects: {
    async asyncIncrement(dispatch) {
      await new Promise((resolve, reject) = > {
        setTimeout((a)= > {
          resolve();
        }, 1000);
      });
      dispatch('increment'); ,}}});const App = (a)= > {
  const { get, dispatch } = CounterStore.useStore();
  const count = get(state= > state.count);
  return (
    <div>
      <span>{count}</span>
      <button onClick={()= > dispatch('decrement')}>-</button>
      <button onClick={()= > dispatch(actions => actions.increment)}>+</button>
      <button onClick={()= > dispatch('asyncIncrement')}>async+</button>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById('root'));
Copy the code

Online demo to see (Check on CodeSandbox) : Basic | Async

This code covers all of Stamen’s APIS, and the core concepts include:

  • As simple as possible Api, no connect, Provider
  • Use React Hooks, discard hoc and Render props
  • Multiple stores are recommended. Stores are fractal and more flexible

Why not have a Provider?

Stamen defaults to multiple stores, which is more flexible and simple, so there is no need to use a Provider package.

Why Reack Hooks?

Write code with React Hooks is more readable, more maintainable, better support for Typescript, hoc is bad support for Typescript, render props is a bit anti-human, Of course, hoc has other disadvantages compared to render props and React Hooks, which are documented in the Hooks documentation.

There was a bit of code where using render props generated too much nesting:

const Counter = create({ count: 0 });
const User = create({ name: 'foo' });
const Todo = create({ todos: []});const App = (a)= > (
  <div>
    {User.get(user => (
      <div>
        <span>{user.name}</span>
        <div>
          {Todo.get(todo => (
            <div>
              {todo.todos.map(item => {
                <div>
                  <span>{item.name}</span>;
                  <span>{Counter.get(s => s.count)}</span>
                </div>;
              })}
            </div>
          ))}
        </div>
      </div>
    ))}
  </div>
);
Copy the code

React-hooks react-hooks react-hooks react-hooks react-hooks

const counterStore = CounterStore.useStore();
const userStore = UserStore.useStore();
const todoStore = TodoStore.useStore();

const count = counterStore.get(s= > s.count);
const name = userStore.get(s= > s.name);
const todos = TodoStore.get(s= > s.todos);

const App = (a)= > (
  <div>
    <span>{name}</span>
    <div>
      {todos.map(item => {
        <div>
          <span>{item.name}</span>
          <span>{count}</span>
        </div>;
      })}
    </div>
  </div>
);
Copy the code

The following is perfect support for Typescript. The previous is hoc support for Typescript, render props are poorly readable, and React Hooks balance both nicely.

Here are a few giFs to illustrate Stamen’s perfect support for Typescript.

Figure 1: Hover over the variables state and Action to see their full type definitions. Unlike using hoc like connect, you don’t write any type definitions, everything is automatically typed down.

Figure 2: Automatic completion of state.

Figure 3: Dispatch supports two types of parameters: a string (the name of an action function) and an actionSelector function (similar to Redux’s stateSlector). The latter is recommended for better development experience.

Figure 4: Use actionSelector to easily jump to action function definitions for safe refactoring and renaming operations.

Stamen’s Api is very simple, and you can look directly at the type definition:

interface Opt<S, R, E> {
  state: S; reducers? : R; effects? : E; } declarefunction createStore<S.R extends Reducers<S>, E extends Effects> (opt: Opt<S, R, E>) :{
  useStore: (a)= > {
    get: <P>(selector: Selector<S, P>) => P; dispatch: (action: ActionSelector<R, E> | keyof R | keyof E, payload? : any) => void; }; };Copy the code

For more information on how to use Stamen, see Github: Stamen