如何利用 React Hooks 管理全局状态

本文写于 2020 年 1 月 6 日

React 社区最火的全局状态管理库必定是 Redux,但是 Redux 本身就是为了大型管理数据而妥协设计的——这就会让一些小一点的应用一旦用上 Redux 就变得复杂无比

后来又有了 Mobx,它对于小型应用的状态管理确实比 Redux 简单不少。可是不得不说 Mobx+React 简直就是一个繁琐版本的 Vue。所以我也不太喜欢,不如直接用 Vue3。

总而言之,不管是 react-redux 还是 mobx,他们使用的时候都非常复杂,甚至需要你去组件函数或是组件类上修修改改,从审美角度上来说就令人不太喜欢。

直到后来某一天用了 Angular,我就开始对 SOA 产生好感,ng 的 Service 的写法与依赖注入控制反转着实惊艳到了我。

Service 是 Angular 的逻辑复用方法,并且解决了共享状态的问题,那 React 的自定义 Hook 可以达到类似的效果嘛?

可以,并且会比 Angular 更简洁!!!

什么是 Service

我们先来想一下,Service 到底是什么?

  • Service 包含 n 个方法;
  • Service 包含有状态;
  • Service 应该是个单例。
  • 这些方法与状态应该是高度整合的,一个 Service 解决的是一个模块的问题。

例如下面这个负责 Todo List 记录的 Service:

class TodoRecordService {
private todoList: Record[] = []; get getTodoList() {
return this.todoList;
} public addRecord(newRecord: Record) {
this.todoList.push(newRecord);
} public deleteRecord(id: string) {
this.todoList = this.todoList.filter((record) => record.id !== id);
} public getRecord(id: string) {
const targetIndex = this.todoList.findIndex((record) => record.id === id);
return { index: targetIndex, ele: this.todoList[targetIndex] };
}
}

自定义 Service

那我们用 React 如何实现一个状态共享的单例呢?

使用 ContextuseContext 即可。

接下来我们做一个最简单的计数器吧:一个负责计数的 button,一个负责显示当前数值的 panel。

const App: React.FC = () => {
return (
<div>
<Button />
<Panel />
</div>
);
};

然后我们来定义我们的 Service:

interface State {
count: number;
handleAdd: () => void;
} export const CountService = createContext<State>(null);

我们选择让一个 Context 成为一个 Service,这是因为我们可以利用 Context 的特性来进行状态共享,达到单例的效果。

但是光这样还不行,我们想让 count 拥有响应性,就必须使用 useState(或者其他 hook)来创建。

因此需要一个自定义 Hook,并且在 Context.Provider 中传入 Providervalue 值:

interface State {
count: number;
handleAdd: () => void;
} export const CountService = createContext<State>(null); export const useRootCountService = () => {
const [count, setCount] = useState<number>(0);
const handleAdd = useCallback(() => {
setCount((n) => n + 1);
}, []); return {
count,
handleAdd,
};
};

那么在组建中,我们如何使用 Service 呢?

非常简单:

const App: React.FC = () => {
const countService = useContext(CountService); return <div>{countService.count}</div>;
};

所以计数器的完整代码应该这么写:

import { CountService, useRootCountService } from './service/count.service';

const App: React.FC = () => {
return (
<CountService.Provider value={useRooCountService()}>
<div>
<Button />
<Panel />
</div>
</CountService.Provider>
);
}; // Button.tsx
import { CountService } from '../services/global.service'; const Button: React.FC = () => {
// 注意,此处是故意写复杂了,是为了凸显跨组件状态管理的特性
const countService = useContext(CountService);
return <button onClick={() => countService.handleAdd()}>+</button>;
}; // Panel.tsx
import { CountService } from '../services/global.service'; const Panel: React.FC = () => {
const countService = useContext(CountService);
return <h2>{countService.count}</h2>;
};

hooks 与 Service

对于小组件而言,刚刚的写法已经足够了。

但是要知道,Service 是高度集中的某个模块的状态与方法,我们不能保证 Service 的方法可以直接用到组件的逻辑中去。

所以需要我们在组件内部对于逻辑进行二次拼装。

但是把逻辑直接写到组件里面是一件非常恶劣的事情!!!

幸好,React 有了 hooks 让我们去抽离逻辑代码。

const useLogic1 = () => {
// 在 hook 中获取服务
const xxxService = useContext(XxxService);
// ...
const foo = useCallback(() => {
// ...
xxxService.xxxx();
// ...
}, []); return {
// ...
foo,
};
}; const SomeComponent: React.FC = () => {
// 复用逻辑
const { a, b, foo } = useLogic1(someParams);
const { c, bar } = useLogic2(); return (
<div>
<button onClick={() => bar()}>Some Operation</button>
</div>
);
};

这种形式的组件,便是我们的目标。

(完)

如何利用 React Hooks 管理全局状态的更多相关文章

  1. Sentry 开发者贡献指南 - 前端 React Hooks 与虫洞状态管理模式

    系列 Sentry 开发者贡献指南 - 前端(ReactJS生态) Sentry 开发者贡献指南 - 后端服务(Python/Go/Rust/NodeJS) 什么是虫洞状态管理模式? 您可以逃脱的最小 ...

  2. 关于React Hooks,你不得不知的事

    React Hooks是React 16.8发布以来最吸引人的特性之一.在开始介绍React Hooks之前,让咱们先来理解一下什么是hooks.wikipedia是这样给hook下定义的: In c ...

  3. 手把手教你用ngrx管理Angular状态

    本文将与你一起探讨如何用不可变数据储存的方式进行Angular应用的状态管理 :ngrx/store——Angular的响应式Redux.本文将会完成一个小型简单的Angular应用,最终代码可以在这 ...

  4. 使用 react 的 hooks 进行全局的状态管理

    使用 react 的 hooks 进行全局的状态管理 React 最新正式版已经支持了 Hooks API,先快速过一下新的 API 和大概的用法. // useState,简单粗暴,setState ...

  5. redux是全局状态(数据)的管理机制,局部数据没有意义

    redux是全局状态(数据)的管理机制,局部数据没有意义

  6. react hooks 全面转换攻略(三) 全局存储解决方案

    针对 react hooks 的新版本解决方案 一.redux维持原方案 若想要无缝使用原来的 redux,和其配套的中间件 promise,thunk,saga 等等的话 可以使用 redux-re ...

  7. 小程序全局状态管理,在页面中获取globalData和使用globalSetData

    GitHub: https://github.com/WozHuang/mp-extend 主要目标 微信小程序官方没有提供类似vuex.redux全局状态管理的解决方案,但是在一个完整的项目中各组件 ...

  8. [React Hooks长文总结系列一]初出茅庐,状态与副作用

    写在开头 React Hooks在我的上一个项目中得到了充分的使用,对于这个项目来说,我们跳过传统的类组件直接过渡到函数组件,确实是一个不小的挑战.在项目开发过程中也发现项目中的其他小伙伴(包括我自己 ...

  9. 浅谈React数据流管理

    引言:为什么数据流管理如此重要?react的核心思想就是:UI=render(data),data就是我们说的数据流,render是react提供的纯函数,所以用户界面的展示完全取决于数据层.这篇文章 ...

随机推荐

  1. python学习笔记(一)——Python基础

    一.python 基础语法 python 的解释器在启动时会自动加载一个内建的模块,因此我们在使用 print().input()等函数时不用导入其他模块文件. 基本语法: 每条语句结尾没有分号 定义 ...

  2. simulink中scope图像显示添加图例

    1. 在scope中添加图例 (1)首先打开配置属性(configuration properties),在display下面的show legend前面打钩 这样就允许图例显示出来 (2)对scop ...

  3. Linux网络配置:Nat和桥接模式详解

    Linux网络配置:Nat和桥接模式详解 一.我们首先说一下VMware的几个虚拟设备: Centos虚拟网络编辑器中的虚拟交换机: VMnet0:用于虚拟桥接网络下的虚拟交换机: VMnet1:用于 ...

  4. [译]HTML&CSS Lesson5: 定位

    CSS最大的用处之一就是可以将内容和元素定位到任何我们想要的位置,使我们的设计具有结构,使内容更加易懂. CSS有好几种不同的定位属性,每种都有自己的使用场景.在这节课中我们会通过不同的案例--可复用 ...

  5. html 元素 强制不换行

    html 中 nowrap是用来强制不换行的 在排版中 对包裹plain text的标签使用nowrap属性即刻实现强制不换行. 如:<p nowrap>强制不换行</p>&l ...

  6. android webview与jquery mobile相互通信

    最近做android项目中遇到要在webview中做与js交互相关的东东,涉及到js中调用android本地的方法,于是查了资料整理了一下android和js互相调用的过程.如下demo,demo的主 ...

  7. Linux 0.11源码阅读笔记-文件IO流程

    文件IO流程 用户进程read.write在高速缓冲块上读写数据,高速缓冲块和块设备交换数据. 什么时机将磁盘块数据读到缓冲块? 什么时机将缓冲块数据刷到磁盘块? 函数调用关系 read/write( ...

  8. 【uniapp 开发】工具类 -- MathUtil

    获取随机数 /* ** randomWord 产生任意长度随机字母数字组合 ** randomFlag 是否任意长度 min 任意长度最小位[固定位数] max 任意长度最大位 ** ** 生成6-1 ...

  9. CSS3中有哪些新特性

    新增各种CSS选择器 (: not(.input):所有 class 不是"input"的节点) 圆角 (border-radius:8px) 多列布局 (multi-column ...

  10. linux磁盘概述

    硬盘简史 世界上第一块硬盘出生在1956年,至今已有61年半个多世纪的历史.它由IBM公司制造,世界上第一块硬盘:350RAMAC.盘片直径为24英寸,盘片数为50片,重量则是上百公斤,相当于两个冰箱 ...