react之redux状态管理
1、传统MVC框架的缺陷
模型(model)-视图(view)-控制器(controller)的缩写
V
即View视图:用户看到并与之交互的界面。
M
即Model模型是管理数据:很多业务逻辑都在模型中完成。在MVC的三个部件中,模型拥有最多的处理任务。
C
即Controller控制器:接受用户的输入并调用模型和视图去完成用户的需求,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。
缺点
MVC框架的数据流很理想,请求先到Controller, 由Controller调用Model中的数据交给View进行渲染.
但是在实际的项目中,又是允许Model和View直接通信的。
2、Flux
在2013年,Facebook让React
亮相的同时推出了Flux框架,React
的初衷实际上是用来替代jQuery
的,Flux
实际上就可以用来替代Backbone.js
,Ember.js
等一系列MVC
架构的前端JS框架。
其实Flux
在React
里的应用就类似于Vue
中的Vuex
的作用,但是在Vue
中,Vue
是完整的mvvm
框架,而Vuex
只是一个全局的插件。
React
只是一个MVC中的V(视图层),只管页面中的渲染,一旦有数据管理的时候,React
本身的能力就不足以支撑复杂组件结构的项目,在传统的MVC
中,就需要用到Model和Controller。Facebook对于当时世面上的MVC
框架并不满意,于是就有了Flux
, 但Flux
并不是一个MVC
框架,他是一种新的思想
- View: 视图层
- ActionCreator(动作创造者):视图层发出的消息(比如mouseClick)
- Dispatcher(派发器):用来接收Actions、执行回调函数
- Store(数据层):用来存放应用的状态,一旦发生变动,就提醒Views要更新页面
Flux的流程:
- 组件获取到store中保存的数据挂载在自己的状态上
- 用户产生了操作,调用actions的方法
- actions接收到了用户的操作,进行一系列的逻辑代码、异步操作
- 然后actions会创建出对应的action,action带有标识性的属性
- actions调用dispatcher的dispatch方法将action传递给dispatcher
- dispatcher接收到action并根据标识信息判断之后,调用store的更改数据的方法
- store的方法被调用后,更改状态,并触发自己的某一个事件
- store更改状态后事件被触发,该事件的处理程序会通知view去获取最新的数据
3、Redux
注意:flux、redux都不是必须和react搭配使用的,因为flux和redux是完整的架构,在学习react的时候,只是将react的组件作为redux中的视图层去使用了。
React 只是 DOM 的一个抽象层,并不是 Web 应用的完整解决方案。
有两个方面,它没涉及:
- 代码结构
- 组件之间的通信
2013年 Facebook 提出了 Flux 架构的思想,引发了很多的实现。2015年,Redux 出现,将 Flux 与函数式编程结合一起,很短时间内就成为了最热门的前端架构。
使用场景设计思路
不需要redux情况
- 用户的使用方式非常简单
- 用户之间没有协作
- 不需要与服务器大量交互,也没有使用 WebSocket
- 视图层(View)只从单一来源获取数据
需要redux情况
- 用户的使用方式复杂
- 不同身份的用户有不同的使用方式(比如普通用户和管理员)
- 多个用户之间可以协作
- 与服务器大量交互,或者使用了WebSocket
- View要从多个来源获取数据
从组件层面考虑,什么样子的需要Redux:
- 某个组件的状态,需要共享
- 某个状态需要在任何地方都可以拿到
- 一个组件需要改变全局状态
- 一个组件需要改变另一个组件的状态
Redux的设计思想:
- Web 应用是一个状态机,视图与状态是一一对应的。
- 所有的状态,保存在一个对象里面(唯一数据源)。
Redux的使用的三大原则:
- store:单一数据源
整个应用的 state 被储存在一棵对象结构中,并且这个对象结构只存在于唯一一个 store 中
不能直接去修改此数据源中的数据(数据深拷贝) - State:是只读的
state redux中的state
唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。
action只能是一个对象 - reducer:使用纯函数(reducer)来执行修改
为了描述 action 如何改变 state tree ,你需要编写 reducer,reducer只是一些纯函数,它接收先前的 state 和 action,并返回新的 state
Redux框架的使用
Reducer必须是一个纯函数
纯函数遵守以下一些约束。
- 不得改写参数
- 不能调用系统 I/O 的API
- 不能调用Date.now()或者Math.random()等不纯的方法,因为每次会得到不一样的结果
步骤1:创建统一数据源
- 引入redux 生成createStore
- 创建默认数据 defaultState
- 创建 reduces(state,action){}
- createStore(reduces)创建数据源
//引入创建仓库方法
import {createStore} from 'redux'
//默认数据源数据,不能直接修改
const defaultStore={
count:1
}
//reducer出函数
function reducers(state=defaultStore,action){
if(action.type=='incr'){
return{
count:state.count+1
}
}
return state
}
//创建唯一仓库
const store = createStore(
reducers,
)
export default store
步骤2:组件中获取数据并设置数据
- 获取数据 store.getState()
- 订阅数据 store.subscribe(()=>{this.setState(state=>store.getState())})
- 派发任务 store.dispatch(actionCreator)
import React,{Component} from 'react'
import store from './store'
export default class App extends Component{
constructor(props){
super(props)
this.state = store.getState()
store.subscribe(()=>{
this.setState(state=>store.getState())
})
}
render(){
return(
<div>
<button onClick={this.incr}>++</button>
</div>
)
}
incr=()=>{
const actionCreator=>{
type:'incr',
payLoad:1
}
store.dispatch(actionCreator)
}
}
划分reducer
原因:一个应用只有一个state,单个reducer对数据操作很臃肿,so需要按照不同功能去拆分
注意:
- 分离reducer的时候,每一个reducer维护的状态都应该不同
- 通过store.getState获取到的数据也是会按照reducers去划分的
- 划分多个reducer的时候,默认状态只能创建在reducer中,因为划分reducer的目的,就是为了让每一个reducer都去独立管理一部分状态
react-redux(redux扩展库)
React-Redux是Redux的官方针对React开发的扩展库。
你可以理解为react-redux就是redux给我们提供一些高阶组件
安装
npm i -S redux react-redux
两个核心的api
- Provider: 提供store
根据单一store原则 ,一般只会出现在整个应用程序的最顶层。 - connect: 用于连接容器组件和展示组件
语法格式为:
connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)(component)
一般来说只会用到前面两个,它的作用是:- 把
store.getState()
转化为展示组件的props
- 把
actionCreators
转化为展示组件props
上的方法
- 把
使用
步骤1:定义Provider
- 主程序index.js中定义Provider
- 让全局的组件共享store中的数据
import React from 'react' import ReactDOM from 'react-dom' import { Provider } from 'react-redux' import store from './store' import App from './App' ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') )
步骤2:子程序中使用connect
- 把
store.getState()
转化为展示组件的props
- 把
actionCreators
转化为展示组件props
上的方法
- 传统使用方式
- 装饰器使用方式(推荐使用)
传统使用方式
import React ,{Component} from 'react'
import {connect} from 'react-redux'
import * as actions frm './countAction'
class App exteds Component{
render(){
return (
<div>
{this.props.count}
<button onClick={this.incr}>++</button>
</div>
)
}
incr=()=>{
this.props.incr()
}
}
const mapStateProps=state=>{
return {count:state.count}
}
const mapPropsToDIspatch=dispatch=>{
return{
incr(){
dispatch(actions.incr())
}
}
}
export default connect(mapStateProps,mapPropsToDIspatch)(App)
装饰器使用方式(推荐使用)
import React ,{Component} from 'react'
import {connect} from 'react-redux'
import * as actions frm './countAction'
const mapStateProps=state=>{
return {count:state.count}
}
const mapPropsToDIspatch=dispatch=>{
return{
incr(){
dispatch(actions.incr())
}
}
}
@connect(mapStateProps,mapPropsToDIspatch)
class App exteds Component{
render(){
return (
<div>
{this.props.count}
<button onClick={this.incr}>++</button>
</div>
)
}
incr=()=>{
this.props.incr()
}
}
Redux异步操作(redux-thunk)
通常情况下,action只是一个对象,不能包含异步操作,这导致了很多创建action的逻辑只能写在组件中,代码量较多也不便于复用,同时对该部分代码测试的时候也比较困难.
常见的异步库:
- Redux-thunk
- Redux-saga
- Redux-effects
- Redux-side-effects
- Redux-loop
- Redux-observable
基于Promise的异步库:
- Redux-promise
- Redux-promises
- Redux-simple-promise
- Redux-promise-middleware
安装
npm i -S redux-thunk
使用
在createStore实例store中使用
import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
import reducer from './countReducer'
const store = createStore(
reducer,
applyMiddleware(thunk)
)
export default store
countReducer.js
const incrAction = num=>({
type:'incr',
payload:num
})
export const incr=>90=>dispatch=>{
setTimeout(()=>{
let num=10
dispatch(incrAction(num))
},1000)
}