Home

Awesome

iostore

NPM version build status Test coverage Known Vulnerabilities David deps

背景介绍

由原 react-hooks-model 更名为 iostore

极简的全局数据管理方案,忘掉 reduxstatereduceractionobserver 这些复杂的概念吧。

特性

和之前的方案相比:

TODO

如何使用

安装:

npm install iostore
// or
yarn add iostore

API

引入

import { createStore, useStore } from 'iostore';

createStore(params)

定义一个 store。参数:

普通的 js 对象,必须指定一个namespace

// TodoStore.js
import { createStore } from 'iostore';
createStore({
  namespace: 'TodoStore',
  todos: [],
  getTodoCount() {
    return this.todos.length;
  },
  getNs() {
    return this.namespace;
  },
  ...rest, // 其余自定义的数据和方法
});

// UserStore.js
import { createStore } from 'iostore';
createStore({
  namespace: 'UserStore',
  // 访问其他 store 的方法。
  getTodoCount() {
    return this.stores.TodoStore.getTodoCount();
  },
  ...rest, // 其余自定义的数据和方法
});

useStore()

React 函数式组件中引入所需 store。 无参数。 得益于 ES6 中的解构赋值语法,我们从该方法的返回值中,单独声明所需的 store。

框架会在 store 中注入 stores 对象,用来访问其他 store 的数据。 一般来说,只推荐访问其他 store 的计算数据,不要访问其他 store 中可能导致修改数据的方法。 如果需要修改其他 store 的数据,请在逻辑层/组件内处理。

如下:

const Todo = () => {
  const { TodoStore } = useStore();
  // 之后便可以自由的使用 TodoStore 中定义的方法了。
  const ns = TodoStore.getNs();
  return <div>{ns}</div>;
};

关于 loading

在对交互要求较高的场景下,获取异步方法的执行状态是非常必要的。

例如显示一个 loading 页面告诉用户正在加载数据,按钮上显示一个loading样式提示用户该按钮已经被点击。

当你使用iostore时,这一切变得非常简单。

我们可以非常容易的获取到每一个异步方法的loading状态,甚至可以获取到一个store下有没有异步方法正在执行。

示例如下:

// 定义 store
createStore({
  namespace: 'TodoStore',
  id: 0,
  async inc() {
    await sleep(1000 * 5);
    this.id++;
  },
});

// 获取 loading 状态
const Todo = () => {
  const { TodoStore } = useStore();
  const handleClick = () => TodoStore.inc();
  // TodoStore.loading  store 级别的 loading 状态
  // TodoStore.inc.loading 某个异步方法的 loading 状态
  return (
    <button loading={TodoStore.inc.loading} onClick={handleClick}>
      submit
    </button>
  );
};

完整的 Todo 示例

// TodoStore.js
import store, { createStore, useStore } from 'iostore';
export default createStore({
  namespace: 'TodoStore', // store 命名空间
  id: 0,
  todos: [
    {
      id: 0,
      content: 'first',
      status: 'DOING',
    },
  ],
  addTodo(content) {
    this.id++;
    const todo = {
      id: this.id,
      content,
      status: 'DOING',
    };
    this.todos.push(todo);
  },
  getTodoById(id) {
    return this.todos.filter(item => item.id === id)[0];
  },
  updateTodo(id, status) {
    const todo = this.getTodoById(id);
    if (!todo) return;
    todo.status = status;
  },
  // test async function
  incId: 0,
  async delayIncId() {
    await sleep(1000 * 3);
    this.incId++;
  },
});

// Todos.js
import React, { useRef } from 'react';
import store, { createStore, useStore } from '../src/index';
import todoStore from './TodoStore';

export default () => {
  /**
   * 获取 TodoStore 的几种方式:
   * const { TodoStore } = useStore(); // 更符合 React Hooks 的理念
   * const { TodoStore } = store;
   * const TodoStore = todoStore.useStore();
   */
  const { TodoStore } = useStore();
  const inputEl = useRef(null);
  const handleClick = item => {
    if (item.status === 'DOING') {
      TodoStore.updateTodo(item.id, 'COMPLETED');
    } else if (item.status === 'COMPLETED') {
      TodoStore.updateTodo(item.id, 'DOING');
    }
  };
  const handleAddTodo = () => {
    console.warn('set data within component, should be got console.error : ');
    TodoStore.todos[0].id = 1000;
    const text = inputEl.current.value;
    if (text) {
      TodoStore.addTodo(text);
    }
  };
  console.log('render', 'totos.length:' + TodoStore.todos.length);
  return (
    <div>
      <div data-testid="incid">{TodoStore.incId}</div>
      {!TodoStore.delayIncId.loading ? <div data-testid="incidfinish" /> : ''}

      <div data-testid="incidloading">{TodoStore.delayIncId.loading ? 'loading' : 'completed'}</div>
      <div data-testid="todocount">{TodoStore.todos.length}</div>
      <ul data-testid="todolist">
        {TodoStore.todos.map(item => {
          return (
            <li onClick={() => handleClick(item)} key={item.id}>
              {item.content}
              <span>{item.status}</span>
            </li>
          );
        })}
      </ul>
      <input ref={inputEl} data-testid="todoinput" type="text" />
      <button data-testid="todobtn" onClick={() => handleAddTodo()}>
        add todo
      </button>

      <button data-testid="incbtn" onClick={() => TodoStore.delayIncId()}>
        delay inc id
      </button>
    </div>
  );
};

License

MIT