Star 历史趋势
数据来源: GitHub API · 生成自 Stargazers.cn
README.md

iostore

NPM version build status Test coverage Known Vulnerabilities David deps

背景介绍

由原 react-hooks-model 更名为 iostore

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

特性

  • 总计只有 100 多行代码。
  • 只需要学会两个 API 即可使用,非常简单:createStore()useStore()
  • 像普通的 js 对象一样定义 store
  • 像普通的 js 对象一样使用数据和方法。
  • store 定义的方法内部可任意修改数据,可直接返回数据,支持同步、异步方法。
  • 当数据发生变化时,自动触发组件渲染。基于React Hooks API,实现了完整的单向数据流。
  • 集成异步方法的执行状态管理,目前最优雅的loading状态解决方案之一。
  • store 内部方法可以使用this.stores.TodoStore访问其他的 store 示例,实现更复杂的数据交互。

和之前的方案相比:

  • 不再区分 state, reducer, helper,去掉了这些概念,更简单。
  • 定义 store 就像定义一个普通的 js object 一样,只需要传入一个 namespace 用于区分不同的 store
  • 基于 Proxy 重新设计,数据变化,则自动通知组件,重新渲染。

TODO

  • TypeScript 支持
  • 支持 Vuejs
  • 更多的测试用例

如何使用

安装:

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中有没有异步方法正在执行:Store.loading,返回 true/false
  • 获取store中某个异步方法的 loading 状态:Store.asyncFunction.loading,返回 true/false

示例如下:

// 定义 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

关于 About

极简的全局数据管理方案,基于 React Hooks API
hooksiostorereactreduxstatestore

语言 Languages

JavaScript100.0%

提交活跃度 Commit Activity

代码提交热力图
过去 52 周的开发活跃度
0
Total Commits
峰值: 1次/周
Less
More

核心贡献者 Contributors