[React] 11 - Redux: review
Ref: Redux中文文档
Ref: React 讀書會 - B團 - Level 19 Redux 深入淺出
Ref: React+Redux 分享會
Ruan Yifeng, Redux 架构: 教程一(有代码)、教程二、教程三
课前阅读
背景知识
(1) 首先,复习 Flux:[React] 07 - Flux: react communicates with mongodb
- 原始方案中,pages目录下其实也是组件的效果;
- 右边的flux pattern到底带来了什么?
详见Flux章节和链接。
(2) 之后,让我们开始对 Redux 认真学习。
2015年,Redux 出现,将 Flux 与函数式编程结合一起,很短时间内就成为了最热门的前端架构。
* 解决什么问题?
A如何把参数传给B?react的单向流不好处理。
或者,使用嵌入式写法,比如A触发事件,B监听事件。
但嵌入式写法不推荐。
- React两大不利之处,
- 代码结构
- 组件之间的通信
- 从需求角度看,
- 用户的使用方式复杂
- 不同身份的用户有不同的使用方式(比如普通用户和管理员)
- 多个用户之间可以协作
- 与服务器大量交互,或者使用了WebSocket
- View要从多个来源获取数据
- 从组件角度看,
- 某个组件的状态,需要共享
- 某个状态需要在任何地方都可以拿到
- 一个组件需要改变全局状态
- 一个组件需要改变另一个组件的状态
应用程序状态管理组件
可见,解决组件间的通信便捷性问题。
-
安装包
$ mkdir reduxtest $ cd reduxtest $ npm init -y # 安装 Redux $ npm install redux # 安装 Redux Toolkit, 自带 Redux Core # $ npm install @reduxjs/toolkit
# Redux + Toolkit + 样例代码 = 最佳实践 $ npx create-react-app my-redux-app --template redux # Redux + Toolkit + 样例代码 + Typescript = 最佳实践 $ npx create-react-app my-redux-app --template redux-typescript
一个例子,体现了集中式的 store 的状态管理。
const redux = require('redux');
/**
* 初期状态数据
*/
const initState = {
viewCount: 0,
}
/**
* Reducer函数
* 负责状态数据的更新
*
* @param {*} state 当前状态数据 (旧状态)
* @param {*} action 处理区分/处理类别
*
* @returns 新的状态数据
*/
const reduxReducer = (state = initState, action) => {
console.log("DEBUG")
console.log("DEBUG", "旧状态->", state, "Reducer动作->", action)
let resultState = {}
switch (action.type) {
case 'add':
resultState.viewCount = state.viewCount + 1
break
case 'minus':
resultState.viewCount = state.viewCount - 1
break
case 'multiply':
resultState.viewCount = state.viewCount * 10
break
default:
resultState.viewCount = state.viewCount
break
}
console.log("DEBUG", "新状态->", resultState)
return resultState
}
/**
* 应用程序状态,一般情况下,将 export default store
*/
const store = redux.createStore(reduxReducer);
/**
* 通过 ”订阅状态“ 跟踪 ”数据的变化“,用来完成更新UI等操作
*/
store.subscribe(() => {
console.log("INFO ", "所有订阅者侦听到了状态改变->", store.getState()) // 得到 reduxReducer 返回值显示出来
})
/**
* 调用状态更新处理
*/
// 父组件发出
// store.dispatch({ type: 'add' }) // dispatch 触发了 reduxReducer (就是那个统一管理的大的store),以 update this state.
// 子组件发出
// store.dispatch({ type: 'add' })
// 孙组件发出
// store.dispatch({ type: 'multiply' })
// 曾孙组件发出
// store.dispatch({ type: 'minus' })
React Redux
Ref: https://react-redux.js.org/
- 安装
$ npm install redux react-redux
$ npm start
第一步,新建 ./store/index.js,这里只是构建并导出了store。
const redux = require('redux');
/**
* 初期状态数据
*/
const initState = {
viewCount: 0,
}
/**
* Reducer函数
* 负责状态数据的更新
*
* @param {*} state 当前状态数据
* @param {*} action 处理区分/处理类别
*
* @returns 新的状态数据
*/
const reduxReducer = (state = initState, action) => {
console.log("DEBUG")
console.log("DEBUG", "旧状态->", state, "Reducer动作->", action)
let resultState = {}
switch (action.type) {
case 'add':
resultState.viewCount = state.viewCount + 1
break
case 'minus':
resultState.viewCount = state.viewCount - 1
break
case 'multiply':
resultState.viewCount = state.viewCount * 10
break
default:
resultState.viewCount = state.viewCount
break
}
console.log("DEBUG", "新状态->", resultState)
return resultState
}
/**
* 应用程序状态
*/
const store = redux.createStore(reduxReducer);
/**
* 输出状态对象
*/
export default store;
第二步,以下引用 store,注意使用的方式。
// index.js
import { Provider } from 'react-redux'; import store from './store' ... <Provider store={store}> <App /> </Provider> ...
index.js 是整个工程的入口点。
第三步,创建一个 使用redux 的组件。
因为上图中“大环境”下有 Provier提供了store;所以这里的 useDispatch 知道与哪个store 相关联。
// ./components/LearnRedux.js
import React from "react"; import { useSelector, useDispatch } from "react-redux"; const LearnRedux = () => { const viewCount = useSelector(state => state.viewCount) // ----> 利用redux提供的 两个钩子函数 的通信过程。这里没有提及“容器组件”,所以使用了 useSelector() const dispatch= useDispatch() const btn_click = (type) => { dispatch({ type: type }) } return ( <React.Fragment> <h1>LearnRedux</h1> <h2>{viewCount}</h2> <hr /> <div> <button className="btn btn-primary btn-lg m-1" onClick={() => btn_click('add')}>Add</button> <button className="btn btn-success btn-lg m-1" onClick={() => btn_click('minus')}>Minus</button> <button className="btn btn-danger btn-lg m-1" onClick={() => btn_click('multiply')}>Multiply</button> </div> </React.Fragment> ) } export default LearnRedux;
第四步,将上一步的组件加入到程序中。
// ./App.js
import LearnRedux from "./components/LearnRedux"; ... <LearnRedux></LearnRedux> ....
点击按钮:btn_click --> dispatch --> 把这个消息分发给store,用以更新其中的 state。
通过钩子函数 useSelector,再返回 更新后的 state。
Redux Toolkit