欢迎继续阅读《Taro 小程序开发大型实战》系列,前情回顾:
- 熟悉的 React,熟悉的 Hooks:我们用 React 和 Hooks 实现了一个非常简单的添加帖子的原型
- 多页面跳转和 Taro UI 组件库:我们用 Taro 自带的路由功能实现了多页面跳转,并用 Taro UI 组件库升级了应用界面
- 实现微信和支付宝多端登录:实现了微信、支付宝以及普通登录和退出登录
如果你跟着敲到了这里,你一定会发现现在 的状态管理和数据流越来越臃肿,组件状态的更新非常复杂。在这一篇中,我们将开始用 Redux 重构,因为此次重构涉及的改动文件有点多,所以这一步使用 Redux 重构我们分两篇文章来讲解,这篇是上篇。
如果你不熟悉 Redux,推荐阅读我们的《Redux 包教包会》系列教程:
如果你希望直接从这一步开始,请运行以下命令:
git clone -b redux-start https://github.com/tuture-dev/ultra-club.git
cd ultra-club
本文所涉及的源代码都放在了Github 上,如果您觉得我们写得还不错,希望您能给️这篇文章点赞+Github仓库加星️哦~
双剑合璧:Hooks + Redux
写到这一步,我们发现状态已经有点多了,而且 src/pages/mine/mine.jsx
文件是众多状态的顶层组件,比如我们的普通登录按钮 src/components/LoginButton/index.jsx
组件和我们的 src/components/Footer/index.jsx
组件,我们通过点击普通登录按钮打开登录弹窗的状态 isOpened
需要在 LoginButton
里面进行操作,然后进而影响到 Footer
组件内的 FloatLayout
弹窗组件,像这种涉及到多个子组件进行通信,我们将状态保存到公共父组件中的方式在 React 中叫做 ”状态提升“。
但是随着状态增多,状态提升的状态也随着增多,导致保存这些状态的父组件会臃肿不堪,而且每次状态的改变需要影响很多中间组件,带来极大的性能开销,这种状态管理的难题我们一般交给专门的状态管理容器 Redux 来做,而让 React 专注于渲染用户界面。
Redux 不仅可以保证状态的可预测性,还能保证状态的变化只和对应的组件相关,不影响到无关的组件,关于 Redux 的详细剖析的实战教程可以参考图雀社区的:Redux 包教包会系列文章。
在这一节中,我们将结合 React Hooks 和 Redux 来重构我们状态管理。
安装依赖
首先我们先来安装使用 Redux 必要的依赖:
$ yarn add redux @tarojs/redux @tarojs/redux-h5 redux-logger
# 或者使用 npm
$ npm install --save redux @tarojs/redux @tarojs/redux-h5 redux-logger
除了我们熟悉的 redux
依赖,以及用来打印 Action 的中间件 redux-logger
外,还有两个额外的包,这是因为在 Taro 中,Redux 原绑定库 react-redux
被替换成了 @tarojs/redux
和 @tarojs/redux-h5
,前者用在小程序中,后者用在 H5 页面中,Taro 对原 react-redux
进行了封装并提供了与 react-redux API 几乎一致的包来让开发人员获得更加良好的开发体验。
创建 Redux Store
Redux 的三大核心概念为:Store,Action,Reducers:
- Store:保存着全局的状态,有着 ”数据的唯一真相来源之称“。
- Action:发起修改 Store 中保存状态的动作,是修改状态的唯一手段。
- Reducers:一个个的纯函数,用于响应 Action,对 Store 中的状态进行修改。
好的,复习了一下 Redux 的概念之后,我们马上来创建 Store,Redux 的最佳实践推荐我们在将 Store 保存在 store
文件夹中,我们在 src
文件夹下面创建 store
文件夹,并在其中创建 index.js
来编写我们的 Store:
import { createStore, applyMiddleware } from 'redux'
import { createLogger } from 'redux-logger'
import rootReducer from '../reducers'
const middlewares = [createLogger()]
export default function configStore() {
const store = createStore(rootReducer, applyMiddleware(...middlewares))
ret