欢迎继续阅读《Taro 小程序开发大型实战》系列,前情回顾:
如果你跟着敲到了这里,你一定会发现现在 的状态管理和数据流越来越臃肿,组件状态的更新非常复杂。在这一篇中,我们将开始用 Redux 重构,因为此次重构涉及的改动文件有点多,所以这一步使用 Redux 重构我们分两篇文章来讲解,这篇是上篇。
如果你不熟悉 Redux,推荐阅读我们的《Redux 包教包会》系列教程:
如果你希望直接从这一步开始,请运行以下命令:
git clone -b redux-start https://github.com/tuture-dev/ultra-club.git
cd ultra-club
本文所涉及的源代码都放在了 Github 上,如果您觉得我们写得还不错,希望您能给❤️这篇文章点赞+Github仓库加星❤️哦~
写到这一步,我们发现状态已经有点多了,而且 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,Action,Reducers:
好的,复习了一下 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))
return store
}
可以看到,我们导出了一个 configureStore
函数,并在其中创建并返回 Store,这里我们用到了 redux-logger
中间件,用于在发起 Action 时,在控制台打印 Action 及其前后 Store 中的保存的状态信息。
这里我们的 createstore
接收两个参数:rootReducer
和 applyMiddleware(...middlewares)
。
rootReducer
是响应 action
的 reducer
,这里我们导出了一个 rootReducer
,代表组合了所有的 reducer
,我们将在后面 "组合 User 和 Post Reducer“ 中讲到它。
createStore
函数的第二个参数我们使用了 redux
为我们提供的工具函数 applyMiddleware
来在 Redux 中注入需要使用的中间件,因为它接收的参数是 (args1, args2, args3, ..., argsn)
的形式,所以这里我们用了数组展开运算符 ...
来展开 middlewares
数组。
创建完 Store 之后,我们接在来编写 Reducer。回到我们的页面逻辑,我们在底部有两个 Tab 栏,一个为 “首页”,一个为 “我的”,在 ”首页“ 里面主要是展示一列文章和允许添加文章等,在 ”我的“ 里面主要是允许用户进行登录并展示登录信息,所以整体上我们的逻辑有两类,我们分别将其命名为 post
和 user
,接下来我们将创建处理这两类逻辑的 reducers。
Reducer 的逻辑形如 (state, action) => newState
,即接收上一步 state 以及修改 state 的动作 action,然后返回修改后的新的 state,它是一个纯函数,意味着我们不能突变的修改 state。
推荐:
newState = { ...state, prop: newValue }
不推荐:
state.prop = newValue
Redux 推荐的最佳实践是创建独立的 reducers
文件夹,在里面保存我们的一个个 reducer 文件。我们在 src
文件夹下创建 reducers
文件夹,在里面创建 user.js
文件,并加入我们的 User Reducer 相应的内容如下:
import {
SET_LOGIN_INFO, SET_IS_OPENED } from '../constants/'
const INITIAL_STATE = {
avatar: '',
nickName: '',
isOpened: false,
}
export default function user(state = INITIAL_STATE, action) {
switch (action.type) {
case