③ Redux
Redux
react 和 redux 实际上是两个独立的产品,项目中可以使用 redux,也可以使用 redux 而不使用 react
- redux 是一种新型的前端架构模式
1 Redux 设计和使用的三个原则
- store 必须是唯一的
- 只有 store 能改变自己的内容
reducer 可以接受 state,但是绝对不能修改 state
- Reducer 必须是一个纯函数
纯函数:不依赖执行上下文,也不影响上下文的变量,输出只由输入决定
2 基本概念
2.1 核心对象 Store
store
是redux 存储区
(一个保存数据的地方),规定一个应用只能有一个store
,store
中的数据可以被直接访问,但只能通过提供的reducer
进行更新
1. 生成数据仓库的方法
createStore(reducer)
2. 常用方法
store.getState()
-- 获取仓库最新 state 状态(数据)store.dispatch(action)
-- 修改 state 的唯一方法store.subscribe(fn)
-- 监听数据修改
componentDidMount() {
// 获取store数据并监听修改(手动)
let cartLen = store.getState().goodslist.length;
store.subscribe(() => {
cartLen = store.getState().goodslist.length;
this.setState({
cartLen
})
})
this.setState({
cartLen,
currentPath: this.props.location.pathname
})
}
2.2 数据存储 State
state 为数据状态(快照,即数据在某个时间点的状态),State 改变则 View 改变
store.getState()
2.3 状态更新提交 Dispatch
store.dispatch(action)
2.4 状态更新参数 Action
Action 用于定义如何改变 state,是用户改变 state 的唯一方式
1. 格式:{ type: 'UPDATE_CART', payload }
- type:一个简单的字符串常量
- payload:用于更新状态的数据
store.dispatch({ type: 'add_to_cart', payload: goods })
2. Action Creator
每次编写 action 对象比较麻烦,可以封装一个函数用于生成 action
export function updateCart(todo) {
return {
type:'UPDATE_CART',
payload:todo
}
}
3. bindActionCreators
利用 redux 的 bindActionCreators 把 ActionCreator 中的所有方法(export default 中的方法)绑定到组件 props 并自动隐式调用 dispatch(action)
import { bindActionCreators } from redux;
import { connect } from 'react-redux';
import ActionCreator from 'actions';
//...
MyComponent = connect(
state => state,
dispatch => bindActionCreators(ActionCreator, dispatch)
)(MyComponent)
2.5 状态更新逻辑 Reducer(修改数据的方式)
reducer 必须是一个纯函数,用于指定 state 修改逻辑,它接收当前 state 和 action 作为参数,并根据 state 和 action 的值返回新的 state
//设置默认值
let defaultState = { goodslist: [], step: 0 }
let cartReducer = (state = defaultState, action) => {
switch(action.type) {
case 'UPDATE_CART':
return { ...state, goodslist: action.payload }
default:
return state;
}
}
1. 处理多个 Reducer:combineReducers
只对 state 有影响,对 patch 无影响
import { createStore, combineReducers } from 'redux'
import productsReducer from './productsReducer'
import cartReducer from './cartReducer'
// 合并 Reducer
const allReducers = {
products: productsReducer,
shoppingCart: cartReducer
}
const rootReducer = combineReducers(allReducers)
let store = createStore(rootReducer)
3 rudex 使用步骤
- 创建
store
(需要指定 state 与 state 的修改方式)- 需要定义
state
- 需要定义
reducer
- 需要定义
- 定义
state
:状态(数据) - 定义
reducer
- 修改
state
的方式 - 必须返回一个新的
state
- 修改
- 使用
- 获取:
store.getState()
- 修改:
store.dispatch(action)
- 获取:
4 react-redux
利用 context 把 store 上的数据传入到 react 组件中
- react-redux 提供了一些接口,用于 redux 的状态和 react 的组件展示结合起来,以实现状态和视图的一一对应
4.1 使用步骤
1. 在最外层使用 <Provider/>
组件,并共享 Store
- 组件
<Provider>
react-redux
提供provider
组件,接收redux
的store
作为props
,并将其声明为context
的属性之一
可结合connect
方法可以让容器组件方便获取state
和dispatch
ReactDOM.render(
<Provider store={store}>
<HashRouter>
{/* <Route component={App}/> */}
<App/>
</HashRouter>
</Provider>
, document.querySelector('#app')
)
2. 使用 conect
高阶组件映射数据到组件的 props
- 高阶组件
connect()
connect
方法为react-redux
核心方法,它把react
组件与redux
中的store
真正连接在一起,connect
方法接收两个参数(function
类型),用于定义了ui组件的业务逻辑,返回容器组件
- 格式:
connect([mapStateToProps], [mapDispatchToProps])(Component)
函数柯里化
mapStateToProps
将 state 中的数据映射到 props
- 当 state 更新时,会自动重新渲染
- 必须返回一个对象
const mapStateToProps = function(state) {
//映射state数据到App组件的props
console.log('state:', state);
return {
cartLen: state.goodslist.length
}
}
mapDispatchToProps
将操作映射到 props,映射成 action
const mapDispatchToProps = dispatch => {
return {
clear() {
dispatch({ type: 'clear_cart' })
},
changeQty(goods_id, goods_qty) {
dispatch({ type: 'change_goods_qty', payload: { goods_id, goods_qty }})
},
remove(goods_id) {
dispatch({ type: 'remove_from_cart', payload: { goods_id }})
}
}
}
5 生成器 & 迭代器
5.1 如何处理ajax请求
ES6新特性
5.2 Generator 生成器
return
:结束并返回值yield
:暂停并返回值
5.3 Iterator 迭代器
for...of
:能遍历具有迭代器特性的数据next()
:返回数据格式为{ value, done }
5.4 生成器函数返回一个迭代器
function* name(){}
执行生成器函数的代码要使用
next()
方法
yield
暂停之后用next()
方法继续执行
function* show() {
console.log(1)
yield 'zhangsan'
console.log(2)
yield 18
console.log(3)
return 100
console.log(4)
}
for...of
默认不能遍历对象,但给对象添加迭代器就可以遍历
添加
Symbol.iterator
和next
属性
let zhangsan = {
age: 18
}
Object.defineProperties(zhangsan, {
[Symbol.iterator]: {
value() {
var idx = 0;
return {
next: () => {
var done = idx >= Object.keys(this.length)
var key = Object.keys(this)[idx]
var value = this[key]
idx++
return {
value: [key, value],
done
}
}
}
}
}
})
6 redux-saga中间件
引入 saga 即为函数
6.1 saga 的使用步骤
- 创建
saga
中间件 - 将该中间件连接至
store
vue 的 action -> redux 只提供接口 applyMiddleWare
createStore
方法中只能有一个中间件- 第一种:compose 组合中间件
- 第二种:调试工具内部已有 compose
- 运行
saga
配置
redux-saga
内部会自动调用next()
,(等结果返回后调用)
- 如何编写saga配置文件?
6.2 使用 saga 需要考虑两个问题
- 如何监视用户操作?(如:购物车数量修改)
- 如何等待异步操作的结果返回,并触发reducer修改?--
yiled
- 监听
takenEvery
-- 可同时触发多次takeLatest
-- 仅允许触发最后一次