Star 历史趋势
数据来源: GitHub API · 生成自 Stargazers.cn
README.md
iostore
由原 react-hooks-model 更名为 iostore。
极简的全局数据管理方案,忘掉 redux、state、reducer、action、observer 这些复杂的概念吧。
特性
- 总计只有 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