Redux流程图
redux中的角色
- Store:存储状态,访问状态,提交状态更新,监听状态变更
- Reducer:状态更新的执行者,是纯函数
- Action:存放数据的对象,消息的载体,只能被别人操作,自己不能进行任何操作
- redux的基本使用
创建store和reducer
// store.js
// 引入createStore函数
import {createStore} from 'redux'
// reducer具体状态更新的执行者, reducer是纯函数:传入的值和返回值是固定的可预测的。
const counterReducer=(state=0,action)=>{
switch (action.type) {
case 'add':
return state+1
case 'minus':
return state-1
default:
return state
}
}
// 创建store=>将reduce传给store
const store = createStore(counterReducer)
export default store
在组件中使用redux
// reduxTest组件中使用store
import React, { Component } from "react"
import store from '../store'
export default class ReduxTest extends Component {
render() {
return (
<div>
<!--获取状态-->
<p>{store.getState()}</p>
<div>
<button onClick={() => store.dispatch({type:'add'})}>+</button>
<button onClick={() => store.dispatch({type:'minus'})}>-</button>
</div>
</div>
);
}
}
最原始的使用方法,监听store变化更新render函数
// 连接store和component
// index.js
import store from './store'
function render(){
ReactDOM.render(<ReduxTest/>, document.querySelector('#root'))
}
render()
// 监听:store的订阅方法,当store有变化时执行回调函数
store.subscribe(render)
总结步骤:
- 创建reducer
- 创建store
- 获取getState
- 修改状态dispatch
- 订阅状态变更subscribe
- react-redux整合
-
提供了两个api
- Provider顶级组件,提供数据
- connect高阶组件, 提供数据和方法
使用Provider组件封装组件,通过属性传递store
react-redux积层总结
- 创建Action模块
- 创建Reducer模块
- 创建Store模块
- 通过connect方法将React组件和Redux连接起来
- 添加Provide作为项目的根组件,用于数据的存储
// index.js
import {Provider} from 'react-redux'
// 通过属性的方式将store传递
ReactDOM.render((
<Provider store={store}>
<ReduxTest/>
</Provider>
), document.querySelector('#root'))
import React, { Component } from "react"
// connect方法是个工厂函数,类似高阶组件
import {connect} from 'react-redux'
// 装饰器写法 connect返回的才是高阶组件,用于去包装ReduxTest
@connect(
// 状态映射
state => ({num: state}),
// type映射
{
add: ()=>({type:'add'}),
minus: ()=>({type:'minus'})
}
)
class ReduxTest extends Component {
render() {
return (
<div>
{/*用属性获取*/}
<p>{this.props.num}</p>
<div>
<button onClick={()=>this.props.add()}>+</button>
<button onClick={()=>this.props.minus()}>-</button>
</div>
</div>
);
}
}
// 配置函数的作用:将组件中需要的state和dispatch方法映射到属性中
// const mapStateToProps = state => ({num: state})
// const mapDispatchToProps = dispatch => ({
// add: ()=>dispatch({type:'add'}),
// minus: ()=>dispatch({type:'minus'})
// })
// connect本身并不是高阶组件,需要传递两个特别的配置函数,生成的函数才是高阶组件
// 导出高阶组件封装的组件
// export default connect(
// mapStateToProps,
// mapDispatchToProps
// )(ReduxTest)
// 装饰器写法
export default ReduxTest
- Mobx与redux对比
- 难度:redux > mobx
- 工作量:redux > mobx
- 内存开销:redux >mobx
- 状态管理的集中性:redux > mobx
- 样板代码的必要性:redux > mobx
redux中间件机制
利用redux中间件机制可以在实际action响应前执行其他额外的业务逻辑
- 使用redux-thunk和redux-logger实现异步任务和日志记录
//store.js
import {createStore, applyMiddleware} from 'redux'
import logger from 'react-logger'
import thunk from 'redux-thunk'
const counterReducer=(state=0,action)=>{
switch (action.type) {
case 'add':
return state+1
case 'minus':
return state-1
default:
return state
}
}
export default createStore(counterReducer,applyMiddleware(logger,thunk))
// ReduxTest.js
import React, { Component } from "react"
import {connect} from 'react-redux'
@connect(
// 状态映射
state => ({num: state}),
// type映射
{
add: ()=>({type:'add'}),
minus: ()=>({type:'minus'}),
// 本身是个函数,函数执行后又返回一个函数。返回的函数传递一个dispatch,在异步操作后可以dispatch一个修改state的action
// 正常的action返回的是一个对象如上add,minus=>这里使用trunk可以处理返回函数类型,只要是函数就当作异步处理
asyncAdd: ()=> (dispatch) => {
// 模拟异步操作
setTimeout(()=>{
// 异步结束后手动执行dispatch
dispatch({type:'add'})
},1000)
}
}
)
class ReduxTest extends Component {
render() {
return (
<div>
<p>{this.props.num}</p>
<div>
<button onClick={()=>this.props.add()}>+</button>
<button onClick={()=>this.props.minus()}>-</button>
<button onClick={()=>this.props.asyncAdd()}>asyncAdd</button>
</div>
</div>
);
}
}
export default ReduxTest
// index.js
import {Provider} from 'react-redux'
// 通过属性的方式将store传递
ReactDOM.render((
<Provider store={store}>
<ReduxTest/>
</Provider>
), document.querySelector('#root'))
- 整合redux,将counter相关的reducer和逻辑放到一起, 简化ReduxTest和index.js。所有与counter相关的业务逻辑都放到counter.redux.js中。
// store/counter.redux.js
export default (state = 0, action) => {
switch (action.type) {
case "add":
return state + 1;
case "minus":
return state - 1;
default:
return state;
}
};
function add() {
return { type: "add" };
}
function minus() {
return { type: "minus" };
}
function asyncAdd() {
return (dispatch, getState) => {
console.log(getState());
// 模拟异步操作
setTimeout(() => {
dispatch({ type: "add" });
}, 1000);
};
}
export {add, minus, asyncAdd}
// store/index.js
import {createStore, applyMiddleware} from 'redux'
import logger from 'react-logger'
import thunk from 'redux-thunk'
import counterReducer from './counter.redux.js'
export default createStore(counterReducer,applyMiddleware(logger,thunk))
// ReduxTest.js
import React, { Component } from "react";
// import store from '../store'
import { add, minus, asyncAdd } from "../store/counter.redux";
import { connect } from "react-redux";
@connect(
state => ({ num: state.counterReducer }), // 状态映射
{ add, minus, asyncAdd } // action 映射
)
class ReduxTest extends Component {
render() {
return (
<div>
<p>{this.props.num}</p>
<div>
<button onClick={() => this.props.minus()}>-</button>
<button onClick={() => this.props.add()}>+</button>
<button onClick={() => this.props.asyncAdd()}>asyncAdd</button>
</div>
</div>
);
}
}
export default ReduxTest;
redux 模块化和redux-saga
// store/index.js
import { createStore, applyMiddleware, combineReducers } from "redux";
import logger from "redux-logger";
import thunk from "redux-thunk";
import counter from "./counter.redux";
import user from "./user.redux";
import createSagaMiddleware from 'redux-saga';
import saga from './sagas';
// 1.创建中间件
const mid = createSagaMiddleware();
// 2.应用中间件
const store = createStore(
// reducer模块化
combineReducers({ counter, user }),
applyMiddleware(logger, mid)
)
mid.run(saga);
export default store;
// store/sagas.js 异步处理中间件redux-saga
import { call, put, takeEvery } from "redux-saga/effects";
const api = {
login() {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) {
resolve({ id: 1, name: "jerry" });
} else {
reject("用户名或密码错误");
}
}, 1000);
});
}
};
// work saga 实现请求逻辑的地方
// 这是使用了generator生成器
function* login(action) {
try {
// 用call调用异步方法api.login,也可以传参call(api.login,{})
const result = yield call(api.login);
yield put({ type: "login", result });
} catch (error) {
yield put({ type: "login_failure", message: error.message});
}
}
// 监听器
function* mySaga(){
yield takeEvery('login_request', login);
}
export default mySaga;
// store/user.redux.js
const initialState = {
isLogin: false
};
export default (state = initialState, { type, payload }) => {
switch (type) {
case 'login':
return {isLogin: true};
default:
return state;
}
};
// for redux-thunk
// export function login(){
// return (dispatch)=>{
// // mock一个异步登录
// setTimeout(()=>{
// dispatch({type:'login'})
// }, 1000)
// }
// }
// for saga
export function login(){
return {type:'login_request'}
}
redux调试工具
redux-saga和redux-trunk的区别
redux-trunk可以接收function类型的action,saga则是纯对象action解决方案 saga使用generator解决异步问题,非常方便用同步方法编写异步代码