Redux
~~逆战班~ ~
Flux,Redux,React-redux概念
严格意义上来说,React 只是MVC架构中的View层,并不是 Web 应用的完整解决方案,因此,只用 React 没法实现大型应用。为了解决这个问题,2014年 Facebook 提出了 Flux 架构的概念,并在2015年推出Redux,将 Flux 与函数式编程结合一起,在很短时间内就成为了最热门的前端架构。
-
什么是Redux:Redux 是一种新型的前端“架构模式”(Flux 架构的一种变种),它不关注你到底用什么库,你可以把它应用到 React 和 Vue,甚至跟 jQuery 结合都没有问题。
-
React和Redux:事实上是两个独立的产品,项目中可以使用 React 而不使用
Redux ,也可以使用 Redux 而不使用 React -
什么是React-redux:就是把 Redux 这种架构模式和 React.js 结合起来的一个库,是 Redux 架构在 React.js 中的体现,利用这个库能够大大简化代码的书写
什么时候需要Redux
Redux作者:如果你不知道是否需要 Redux,那就是不需要它,只有遇到 React 实在解决不了的问题,你才需要 Redux
- 某个组件的状态,需要共享
- 某个状态需要在任何地方都可以拿到
- 在一个组件中需要改变全局状态
- 在一个组件中需要改变另一个组件的状态
Redux设计和使用的三项基本原则
- store是必须是唯一的
- 只有store能改变自己的内容
reducer 可以接受state,但是绝对不能修改state
- Reducer必须是一个纯函数
纯函数指的是,给固定的输入,就一定会有固定的输出,而且不会有任何副作用
Redux 的工作流程
基本概念
核心对象:Store
store是Redux存储区(一个保存数据的地方),你可以把它看成一个仓库,规定一个应用只能有一个Store,store中的数据可以被直接访问,但只能通过提供的reducer进行更新。
- 生成数据仓库方法:
createStore(reducer[,initState][,middleware])
import { createStore } from 'redux';
const store = createStore(reducer);//reducer为一个纯函数,用于设定state修改逻辑(如何修改state中的数据)
- 常用方法
- store.getState() 获取仓库最新状态(数据)
- store.dispatch(action) 操作数据
- store.subscribe(fn) 监听数据修改
数据存储:State
state为数据状态(快照,即数据在某个时间点的状态),State改变则View改变
store.getState();//获取某一个数据状态(对store生成快照)
状态更新提交:Dispatch
修改数据操作
store.dispatch(action);//是 View 发出 Action 的唯一方法
状态更新提交参数:Action
Action用于定义如何改变state,是用户改变 State 的唯一方式,
- 格式:
{type:'UPDATE_CART',payload}
- type: 一个简单的字符串常量,例如ADD, UPDATE, DELETE等。
- payload: 用于更新状态的数据。
- 使用方式:
store.dispatch({type:'UPDATE_CART',num:100});
Action Creator
每次编写action对象比较麻烦,可以封装一个函数用于生成action
export function updateCart(todo){
return {
type:'UPDATE_CART',
payload:todo
}
}
- 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)
状态更新逻辑: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;
}
}
- 处理多个Reducer:combineReducers
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);
React-Redux
为了方便使用,Redux 的作者封装了一个 React 专用的库 React-Redux,它提供了一些接口,用于Redux的状态和React的组件展示结合起来,以用于实现状态与视图的一一对应
组件分类
根据功能不同,React-Redux 将所有组件分成两大类:
1)UI 组件
- 职责简单,只负责 UI 的呈现,不带有任何业务逻辑
- 没有状态(即不使用this.state这个变量)
- 所有数据都由参数(props)提供
- 不使用任何 Redux 的 API
2)容器组件
- 负责管理数据和业务逻辑,并把数据通过prop传入UI组件(自身不负责 UI效果)
- 带有内部状态
- 使用 Redux 的 API
React-Redux 规定,所有的 UI 组件都由用户提供,容器组件则是由 React-Redux 自动生成
如果一个组件既有 UI 又有业务逻辑,那怎么办?解决方案是,将它拆分成下面的结构:
- 外面是一个容器组件,里面包了一个UI 组件。
- 前者负责与外部的通信,将数据传给后者,由后者渲染出视图
组件<Provider>
- React-Redux 提供Provider组件,接受Redux的store作为props,并将其声明为context的属性之一
- 可结合
connect
方法可以让容器组件方便获取state和dispatch
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider>,
document.getElementById('app')
)
高阶组件connect()
connect方法为React-Redux核心方法,它把react组件与redux中的Store真正连接在一起,connect方法接受两个参数(Function类型),用于定义了 UI 组件的业务逻辑,返回容器组件
- 格式:connect([mapStateToProps],[mapDispatchToProps])
mapStateToProps
顾名思义,将state中的数据映射到UI组件的props,
-
特点:每当state更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染
-
负责组件的输入逻辑,即将state映射到 UI 组件的参数(props)
-
必须返回一个对象
mapDispatchToProps
- 负责组件的输出逻辑,即将用户对 UI 组件的操作映射成 Action
- 必须返回一个对象
function mapStateToProps(state,ownprops) {
//state:redux中的state
//ownprops: Cart组件自身的props
return {
//将state中购物车页面的goodslist数据映射到props,Cart组件中通过props.data访问
data: state.shoppingCart.goodslist
}
}
function mapDispatchToProps(dispatch,ownprops) {
// dispatch: redux中的dispatch方法
// ownprops:同上
return {
onChangeQty: (action) => dispatch(action),
onRemoveGoods: (action) => dispatch(action),
}
}
Cart = connect(mapStateToProps,mapDispatchToProps)(Cart);
redux中间件
redux中的action仅支持原始对象(plain object),处理有副作用的action,需要使用中间件。中间件可以在发出action,到reducer函数接受action之间,执行具有副作用的操作
常用中间件
- redux-chunk
- redux-promise
- redux-saga
- Generator生成器函数
- yield
- Iterator
- Effect
- call
- apply
- put
- takeEvery
- takeLatest
- Generator生成器函数
使用中间件
- 利用applyMiddleware接受中间件(可同时接受多个中间件)
- 通过createStore的第3个参数连接中间件与store
import {createStore,applyMiddleware} from 'redux';
import createSagaMiddleware from 'redux-saga';
// 1.引入自定义saga配置文件
import rootSaga from './rootSaga.js';
// 2.创建saga中间件
const sagaMiddleware = createSagaMiddleware();
// 3.将 sagaMiddleware 连接至 Store
let enhancer = applyMiddleware(sagaMiddleware)
const store = createStore(reducer,enhancer);
// 4.运行 Saga配置
sagaMiddleware.run(rootSaga);
多个enhancer使用redux.compose组合成单个enhancer
import {createStore,applyMiddleware,compose} from 'redux'
let enhancer = compose(...enhancer);
const store = createStore(reducer,enhancer);
调式Redux程序
- 在谷歌应用商店下载redux-devtools
- 安装redux-devtools-extension
npm install -save-dev redux-devtools-extension
- 引入并使用
// 单独使用
import {composeWithDevTools } from 'redux-devtools-extension';
const store = createStore(rootReducer,composeWithDevTools());
export default store;
// 与saga一起使用
import {createStore,applyMiddleware,compose} from 'redux'
import rootSaga from './rootSaga.js';
const sagaMiddleware = createSagaMiddleware();
let enhancer = composeWithDevTools(applyMiddleware(sagaMiddleware))
// 或使用compose
// let enhancer = compose(applyMiddleware(sagaMiddleware),composeWithDevTools())
const store = createStore(rootReducer,enhancer)
export default store;
- 在Chrome浏览器中调试redux程序