React的异步处理
通常情况下,action只是一个对象,不能包含异步操作,这导致了很多创建action的逻辑只能写在组件中,代码量较多也不便于复用,同时对该部分代码测试的时候也比较困难,组件的业务逻辑也不清晰.
使用中间件了之后,可以通过actionCreator异步编写action,这样代码就会拆分到actionCreator中,可维护性大大提高,可以方便于测试、复用,同时actionCreator还集成了异步操作中不同的action派发机制,减少编码过程中的代码量
- 解决异步action的中间件有哪些?
- redux-thunk
- redux-saga
- redux-promise-middleware
- Redux-effects
- Redux-side-effects
- Redux-loop
- Redux-observable
一、redux-thunk
数据加载时发送请求,使用connect将store中的属性和方法和组件进行关联,其中,mapState对应属性,mapDispatch对应方法,在这里做事件的派发,当涉及异步请求时,可以用redux-thunk。
Redux提供了一个
applyMiddleware
方法作为异步的中间件配置到store中。这里的中间件其实就是action与reducer之间的一个应用。
- 安装
cnpm install redux-thunk -S
cnpm install axios
- 原理
以前的action必须是一个扁平的对象,即使是函数时返回的也是一个对象。当使用了redux-thunk以后,它允许action返回的是一个函数dispatch,在函数中可以发送异步的请求。请求之后的数据,通过dispatch交给reducer。
- 源码
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => {
return next => {
return action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
}
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
- 安装
http-proxy-middleware
配置跨域
cnpm i http-proxy-middleware -D
- 在src下新建setupProxy.js
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = (app) => {
app.use(createProxyMiddleware("/ajax", {
target: "http://m.maoyan.com",
changeOrigin: true
}))
}
api
以猫眼电影中影院的接口为例:
- src / api / request.js
import api from "./index"
import axios from "axios"
export const getClassifyApi = () => axios({
method: "get",
url: api.category
})
- src / api / index.js
export default {
category: "/ajax/filterCinemas?ci=1&optimus_uuid=910E4290AC9B11E9B0072D9FF9AC7F34424DA2065D4542EA980EDC8791BBC609&optimus_risk_level=71&optimus_code=10"
}
action
- src / action / tabTypes.js
export const classIfyType = "CLASSIFY_TYPE"
- src / action / tabActions.js
这里的classifyAsyncAction是一个函数,并且返回一个函数,返回的函数中会有reducers给的dispatch方法。
import { classIfyType } from "./tabTypes";
import { getClassifyApi } from "../../api/request"
export const classifyAsyncAction = () => {
//value是ajax请求之后,传递的参数
var classifyaction = (value) => ({
type: classIfyType,
value
})
return async (dispatch, getState) => {
//异步在这个函数里面去做的
let data = await getClassifyApi();
console.log(getState)
dispatch(classifyaction(data.data));
}
}
- 在src / store / index.js中,通过
applyMiddleware
使用中间件reduxThunk
import { createStore, combineReducers, applyMiddleware } from "redux";
import tab from "./reducers/tab"
import reduxThunk from "redux-thunk"
const reducers = combineReducers({
tab
})
const store = createStore(reducers, applyMiddleware(reduxThunk));
export default store;
- 在src / App.js中发送dispatch验证
import React, { Component } from 'react'
import { connect } from "react-redux";
import { classIfyAsyncAction } from "./action/tab/tabActions"
class App extends Component {
render() {
return (
<div></div>
)
}
componentDidMount() {
this.props.getClassifyData();
}
}
const mapDispatchToProps = (dispatch) => ({
getClassifyData() {
dispatch(classifyAsyncAction())
}
})
export default connect(null, mapDispatchToProps)(App);
- 验证成功之后,正常在reducers中使用即可
import { classIfyType } from "../../action/tab/tabTypes"
let defaultState = {
data: {}
}
export default (state = defaultState, actions) => {
switch (actions.type) {
case classIfyType:
let dataState = JSON.parse(JSON.stringify(state));
dataState.data = actions.value;
return dataState;
}
return state;
}
二、redux-saga
redux-saga 使用了 ES6 的 Generator 功能。它允许action不返回函数,仍然是一个扁平化的对象。
- 安装
cnpm i redux-saga -S
- 在store中配置
import { createStore,applyMiddleware } from "redux"
import reducer from "./reducer"
// 处理异步的文件
import mySaga from "./sagas"
import createSagaMiddleware from "redux-saga";
const sagaMiddleware = createSagaMiddleware();
const store = createStore(reducer,applyMiddleware(sagaMiddleware))
sagaMiddleware.run(mySaga);
export default store;
- sagas.js(计数器案例)
import { put, takeEvery } from "redux-saga/effects"
import { INCREMENT } from "./actionTypes"
function asyncIncrement() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2)
}, 2000)
})
}
function* increment() {
let result = yield asyncIncrement();
console.log('saga increment', result);
// 将异步查询的数据,加入到redux中
yield put({ type: INCREMENT, data: result })
}
function* sagas() {
// 请求数据时,加入到异步队列,加入的是action的名字
yield takeEvery('async_increment', increment)
}
export default sagas;
网友评论