hooks-store
Language switching:English
目录
前言
TL;DR
hooks-store
是使用 React Hooks 给 React 应用提供多 store 全局数据管理方案的一种实现,使用方式上与 Redux 类似。
这一切的实现得益于 React Hooks 16.8 以后推出的两个 hooks:useReducer、useContext。
在 hooks 问世之前,我尝试过使用 Context.Provider 配合 Context.Consumer 以 render props 的方式实现全局数据管理。
当然在实践中遇到很多弊端,所以也没有继续。
useReducer 和 useContext 的出现,足足让我开心了一整天。
它带来了与 Redux 类似的体验:store、dispatch、reducer。
我开始基于 hooks 实践数据管理模式,然后很容地发现了三个问题:
- middleware「中间件」还没有优雅的实现方案
- 如何实现多 store, 以便方便数据管理
- 使用方式并不简单,且需要一定的理解成本
前期,我自以为实现了一个「完美」的方案:使用 React Hooks 代替 Redux。
在这个方案里面,我使用多个 Provider 和多个 Context 实现了多 store 的方案。
而且,我为这样的多 store 方案找到了优雅的 整合模式。
直到现在我也很喜欢这种设计思路,但是基于项目开发的时候,它暴露出一个弊端:不同 store 之间不能共享数据,并且不方便实现 middlewara「中间件」的逻辑等等。
然后,在反复推敲后和实验后,我找到一个新的实践模式:
只使用一个 Provider 就能实现多 store 的方案。
我为这个版本的数据管理方案抽离了 npm 包,诞生了现在的 hooks-store。
从原理和实现上,反而变得简单,并且消除我之前遇到的所有问题。
安装
依赖于 React 16.8 以上的版本
npm i hooks-store
使用
这里展示简单的 react todolist demo 搭建代码,具体实现可以参考 Demo 章节
顶层容器
/* index.jsx */ import React from "react";import ReactDOM from "react-dom";import Provider from "hooks-store"; import storeList from "./storeList";import middlewaras from "./middlewares";import App from "./App"; // 建议项目中使用如下依赖import 'core-js/stable';import 'regenerator-runtime/runtime'; const Root = document; ReactDOM;
数据中心
/* storeList.js */ // 为了方便,这里把两个 store 写在一个文件里面。实际开发中,不建议这样做。 // 使用 immutable 的 setIn 方法import setIn from 'immutable'; // 第一个 Store:// 管理全局通知状态,类似于是否出去加载中。 const noticeInitialState = loading: false; const noticeReducer = state action switch actiontype case 'LOADING_START': // 加载开始 return ; case 'LOADING_STOP': // 加载结束 return ; default: return state; ; // 第二个 Store:// todolist 数据列表。const todolistInitialState = data: ; const todolistReducer = state action switch actiontype case 'TODOLIST_INIT': // 数据初始化 return ; case 'TODOLIST_CLEAR': // 数据清空 return ; default: return state; ; const noticeStore = name: 'notice' initialState: noticeInitialState reducer: noticeReducer; const todolistStore = name: 'todolist' initialState: todolistInitialState reducer: todolistReducer; const storeList = todolistStore noticeStore; ;
middlewara「中间件」
/* middlewares.js */ // 第一个 middleware:// 用来拦截 API 请求const apiFetch = // 如果 action 里面有 api 字段,则表示需要请求服务端数据作为 payload if actionapi // 数据请求前,全局通知进入 loading 状态 ; const serverResponse = await ; // 数据请求后,关闭全局通知 loading 状态 ; const nextAction = ...action payload: actionpayload || serverResponsedata ; delete nextActionapi; // 以新的数据触发一下个 action ; else ; ; // 第二个 middleware:// 用来打印日志const actionLog = next action state console; console; ;; const middlewaras = actionLog apiFetch; ;
子组件
/* App.jsx */ import React from "react";import useStore useDispatch from "hooks-store"; const Loading = // 使用 useStore 方法得到对应的 state 数据 const loading = ; return loading ? "加载中..." : null;; const TodoList = // 使用 useDispatch 的方法得到 dispatch,用来发送 action const dispatch = ; const todoList = ; React; const handleDelete = todo ; ; return <ul> todoListdata </ul> ;; const App = <> <Loading /> <TodoList /> </>; ;
DEMO
- codesandbox 项目:simple-todolist
基于 hooks-store 编写的简单版 todolist - github 项目:typescript-todo-list
基于 hooks-store 配合 typescript 写的 todoList 项目
API
Provider
Provider 作为整个 APP 的父元素。
注:如果是 typescript 项目,需要传递开发者自定义的 State, Action 范形。
- ProviderProps
Provider 组建接收开发者自定义的 stores 和 middlewares 作为参数
- Store
每一个 store 作为一个数据仓库,包含:
- name「唯一的名称」
- initialState「初始化数据,可以是任何非 false 类型」
- reducer「数据出处理中心」
- middleware
中间件作为一个函数,每一个 dispatch(action)
的发送都会经过中间件。
中间件函数接收有三个字段的对象:
- next: 下一个中间件动作分发者。如果已经是最后一个则分发到 reducer
- action:当前触发的 action
- state:全局 store 的数据。注:是所有 store 的数据,而不是某一个 store 的数据,因此可以在中间件中根据全局 store 作出逻辑判断,再进行下一步操作
;
- Reducer
Reducer 作为数据处理器,接收 state「当前 store 的数据」、action「当前触发的 action」;
返回一个新的 state,或者什么都不返回。
注意:这里的 Reducer 和 Redux 的 reducer 不一样:如果 state 没有修改,则不需要返回原来的 state,或者返回 undeinfed。
;
useDispatch
在组件内部通过 useDispatch 得到 dispatch 方法,通过 dispatch(Action)
的可以触发一个 action。
如果想实现异步 dispatch 建议在 middleware 中操作。
;
useStore
在组件内部通过 useStore 得到任何一个 store 获取全局 store 的数据。
useStore 接收一个 namespace 可选参数。
namespace 即任何一个 store 的名称,如果没有传递 namespace 则表示获取所有 store 数据。
;
Typescript 支持
如果项目选择 typescript 静态类型校验,那么 hooks-store 将会友好的支持,这样会保障你的项目更健硕。
要想 hooks-store 与你的 TS 项目友好相处,只需要准备两个类型作为范形「具体传递与使用方式见:API 章节」:
1. State
State 告诉 hooks-store,你的项目的数据是什么样子。
比如:
// 最终范形 State 可以是多个 state 类型的整合;
2. Action
Action 则告诉 hooks-store,你的项目中可能发触发什么样的 action,以便检识别出错误的 action。
比如:
; ; // 最终范形 Action 可以是多个 action 类型的整合;