如何在 React Hooks 项目中进行状态管理?

1. 选手入场

一提起 React 状态管理,不少人就提也不想提。html

Redux: 1 号选手,人气最高的一个,得与 react-redux 一同使用。本身无论异步,因此常听到的什么 redux-thunkredux-saga 是其下属,专门擦屁股的。特色就是难用,若是按照官方文档的写法用,基本就是想死。推荐阅读:www.zhihu.com/question/33…vue

Mobx: 2 号选手,使用监听的方式,与 React 的思想背道而驰,思想有问题,流氓!react

dva: Redux 封装,使用了 redux-saga,generator 的写法,想死。git

Rematch: Redux 封装,相似 dva,model 中分为 reducerseffects,不够简洁。github

广告:因此仍是 🐤 Retalk 最简单,史上最简单的 Redux 封装,走过路过不要错过:github.com/nanxiaobei/…vuex

2. React Hooks 来了

无论怎么说,Hooks 来了,之前的状态管理都是给之前的开发方式用的,为了对 Hooks 有点表示,也就加几个 useBlabla() 完事。redux

与 Hooks 的轻灵相比,它们都显得笨重、老态龙钟、敷衍。bash

有没有专用于 Hooks 的状态管理呢?目前尚未众望所归的,基本都是老选手搞个兼职,有点不三不四。服务器

不够革命。异步

3. 咱们为何须要状态管理?

一个是为了解决相邻组件的通讯问题。

虽然能够经过「状态提高」解决,但有两个问题:

  1. 每次子组件更新,都会触发负责下发状态的父组件的总体更新(使用 Context 也有这个问题),而后写一大堆 PureComponentshouldComponentUpdate,代码还能看吗?React 设计中的糟粕写了个够,太惨。

  2. 逻辑比较多的话,都写在父组件里,代码还能看吗?根本不考虑父组件的感觉。

因此「状态提高」不是个好思路,仍是须要状态管理。

另外,状态管理最重要的一个好处,就是 ——

它能够把「与服务器交互」和「操做数据」的逻辑,从组件中提取出去,组件只用接收处理好的数据便可。这就至关于分离了个 Service 层出去,很好的开发模式。

代码逻辑分离,各司其职,组件去干组件该干的事,这才是最大的好处。

因此咱们须要状态管理。

4. 咱们须要什么样的状态管理?

若是一开始是从 Redux 上手,会发现它提供了一个全局的 store。而后呢?而后它也没说什么话,有什么问题就本身解决吧,我先睡了。

实际业务开发中,遇到的最多见的场景,就是须要区分不一样的「模块」,这是最基本的需求。

Vuex 比较务实,提供了 modules 的概念。

Redux 呢?固然是得靠你本身啦,咱们提供一个理念就好了,这么简单的东西,你就本身去搞吧。

若是不从 API 设计上进行规范,必然会致使无数不一样的实现。(单押 x 2)

CHAOS.

咱们须要什么样的状态管理?

咱们须要符合中国特点社会主义初级阶段国情的状态管理。

咱们须要能够划分不一样的模块,模块是独立的,同时模块间又是能够自由沟通的。

不少状态管理库只考虑了模块的「独立」,可是对「沟通」保持沉默,这是不友好的。

独立,且连通 —— 这才是模块的意义所在。

5. 模块如何划分?

通常来讲,推荐按照基本的路由入口,划分出不一样的模块,这也是 dva 中的思路(它的文件夹干脆就叫 routes)。

这也符合天然的思惟方式,划分路由,也就是自然的认为它们属于不一样的模块。

而每一个状态管理的模块,咱们称之为 "model",建议与路由入口组件绑定起来,每一个入口组件及其子组件,总体对应一个 model。

---A
    index.jsx
    model.js
---B
    index.jsx
    model.js
---C
    index.jsx
    model.js
复制代码

6. 模块如何沟通?

模块是独立的,毋庸置疑,模块模块,不独立还叫模块吗?

那模块如何沟通呢?

在组件中,能够获取到自身 model 和其它的 model,这并不难。

主要是,在 model 内部,自身各个方法须要互相调用,同时须要拿到其它 model 的数据和方法,这才是沟通的意义。

还得足够简单。

这样才是一个良好而完全的设计。

7. Hooks 组件中如何获取 model?

最直接的,咱们但愿在组件中,经过 Hooks 去访问到某个 model。

const { someStateInA, someActionInA } = useModel(a);
// or
const { someStateInA, someActionInA, someStateInB, someActionInB } = useModels(a, b);
复制代码

useModel() 中传入某个 model,这样设计的话,须要哪一个 model,就得在组件中引入 model 的文件,不够自由,不能依赖于必须去引入文件。

只要操做麻烦,代码写的变多,就不是好的设计。

另外 Hooks 是为了让代码更清晰,咱们不能像 Mobx 同样,违背总体系统的设计哲学。

因此 model 获取最好是分开的,useModels(a, b) 一次性引入多个 model 的方式,不够清晰。

不能依赖文件,引入得清晰,因此:

const { someStateInA, someActionInA } = useModel('a');
const { someStateInB, someActionInB } = useModel('b');
复制代码

依赖一个字符串,这样就不须要在每一个组件中引入文件。useModel() 只能访问一个 model,代码足够清晰。

这是咱们须要的设计。

8. model 结构如何设计?

一个 model 整体来讲能够分为两大块,数据(state)和操做数据的方法(reducerseffects)。

reducerseffects,或者 Vuex 中的 mutationsactions,是为了区分直接改变和异步操做,同步函数和异步函数。不够简洁,能不能合并成一个?

答案能够的,咱们称之为 actions

更新 state 的方法,直接注入 model 中,因此一个 action 能够是同步也能够是异步,它只是被当作函数来调用。

因而,model 中分为两块就好:

exports default {
    state: {},
    actions: {},
}
复制代码

9. model 沟通如何设计?

model 沟通,主要是实如今 action 中去访问其它须要的东西。

自由而无限制的沟通。

一个 action 中须要访问到如下:1. 自身 state,2. 自身 action,3. 其它模块 state,4. 其它模块 action,5. 自身 state updater。

简化一下,就是须要访问:1. 自身 model,2. 其它 model,3. 自身 state updater。

再简化一下:1. model,2. 自身 state updater。

因此其实只须要两个方法:getModel()setState()

getModel() 获取自身,getModel('other') 获取其它。

如何拿到这两个方法呢?

  1. 经过 this 访问。但咱们是给 Hooks 设计状态管理,Hooks 就是为了抛弃 this,仍是那句话 —— 不能违背系统的设计哲学

  2. Vuex 中,是在函数第一个参数注入 context,调用时比较反直觉,放弃这种方式。

  3. model 文件中 import 方法进来,太麻烦了,只要操做麻烦,代码写的变多,就不是好的设计

因此惟一的方式(Rematch 的设计中也能看到),就是在参数中注入方法。

也就是把 actions 变为一个函数:

exports default {
    state: {},
    actions: ({ getModel, setState }) => ({
        someAction() {
            const { someState, someOtherAction } = getModel();
        }
    }),
}
复制代码

这里考虑到一个美学问题,getModel() 由于不少 action 都会用到,而实际写出来时,由于 l 高度的问题,又比较丑,因此就把 API 简化为了 model()

model() 获取自身,model('other') 获取其它。

10. flooks 是什么?

因而,flooks,福禄克斯,诞生了:


🍸 flooksgithub.com/nanxiaobei/…


import { setModel, useModel } from 'flooks';

const a = {
    state: {},
    actions: ({ model, setState }) => ({
        someAction() {
            const { someState, someOtherAction } = model();
            ...
            setState(payload);
        }
    }),
}

setModel('a', a);

function A() {
    cosnt { someState, someAction } = useModel('a');
    return (
        ...
    )
}
复制代码

自然的模块化支持。

只有两个 API,setModel()useModel()

加上 actions 的两个参数,model()setState(),就这么多。

足够简单,足够实现一切。

One more thing. 设计哲学

去中心化,去中心化,去中心化。

一切都为了更简单的开发。

再不须要顶层分发 store,不须要 store 文件夹或 store.js 文件。

无需顶层 createStore(),无需顶层 Provider

注册 model 时,无需先在中心化文件中引入 model,自身文件夹内就能够搞定一切。

直接用 setModel() 把组件和 model 链接起来,就这么简单。

模块化,积木同样组合,简洁,灵活,这是咱们的理念。

每一个组件和 model 构成一个自组织,同时它们又能够访问到世界各地。它们不用去有关部门报道,就能在世界的任何角落相遇。

这是咱们的哲学:独立,自由,个体,世界。

相关文章
相关标签/搜索