[React] 12 - Redux: async & middleware

react

Redux 入门教程(二):中间件与异步操作

这里只是简单地了解中间件的概念,对于异步,貌似之后要讲的saga更胜一筹。

 

reducer计算新状态的策略:

  • Action 发出以后,Reducer 立即算出 State,这叫做同步;
  • Action 发出以后,过一段时间再执行 Reducer,这就是异步。

 

 

何为中间件


一、安插中间件的位置

middleware提供的是位于 action 被发起之后,到达 reducer 之前的扩展点。

 

  • 原程序

const Counter = ({ value, onIncrement, onDecrement }) => (

<div>

<h1>{value}</h1>

<button onClick={onIncrement}>+</button>

<button onClick={onDecrement}>-</button>

</div>

);

...............................................................

/**
* 更新过程:

* action + old state ==> new state

* 也可以考虑combineReducers的形式

*/

const reducer = (state = 0, action) => {

switch (action.type) {

case 'INCREMENT': return state + 1;                 // step 2: how to implement this action --> automatically trigger step 3: UI

case 'DECREMENT': return state - 1;

default: return state;

}

};

...............................................................

const store = createStore(reducer);


/**
* 渲染过程:
* 既接收动作,也处理界面更新
* 当然,具体的更新html还要归于具体的html代码,也就是最上面的Counter组件的定义
*/

const render = () => {

ReactDOM.render(

<Counter

value={store.getState()}                       // step 3: render new ui based on new state

onIncrement={() =>store.dispatch({type: 'INCREMENT'})}    // step 1: receive action --> automatically trigger step 2: reducer

onDecrement={() => store.dispatch({type: 'DECREMENT'})}

/>,

document.getElementById('root')

);

};

render();

store.subscribe(render);

 

  • 功能增强

对store.dispatch做了新的定义:

 --- 不仅给store发送信号action。

 --- 而且附带了log的功能。

let next = store.dispatch;

store.dispatch = function dispatchAndLog(action) {

console.log('dispatching', action);

next(action);

console.log('next state', store.getState());

}

再理解如下文字:

(1)Reducer:纯函数,只承担计算 State 的功能,不合适承担其他功能,也承担不了,因为理论上,纯函数不能进行读写操作。

(2)View:与 State 一一对应,可以看作 State 的视觉层,也不合适承担其他功能。

(3)Action:消息的载体,让reducer的纯函数去操作,自己不用干活er。

二、中间件的用法

作为createStore的参数来注册。

applyMiddlewares(...),Redux 的原生方法,作用是将所有中间件组成一个数组,依次执行。

const store = createStore(

reducer,

initial_state,               // 有第二参数则表示整个程序的初始状态

applyMiddleware(logger)

);


const store = createStore(

reducer,

applyMiddleware(thunk, promise, logger)  // 这里的log一定要放在最后

);

 

 

三、实例分析

通过中间件,增强Log的用法。

  • 在reducer之前执行

store.dispatch ==> middleware --> logger ==> "action fired." 

  • 添加 next(action)

其实就是一个多层嵌套返回函数的函数,

柯里化 - 使用箭头的写法在函数式编程,对柯里化更详细的介绍可以看一看这篇 张鑫旭的博客。

第一个(最外层)方法的参数是一个包含dispatch和getState字段(方法)的对象,其实就是store对象,所以也可以写成:

# 一个Redux中间件的基本写法

store => next => action => {

...

};

参数next是一个方法,作用是:通知下一个Redux中间件对这次的action进行处理。

next(action),如果一个中间件中没有执行next(action),则action会停止向后续的中间件传递,并阻止reducer的执行(store将不会因为本次的action而更新)。

 

import { applyMiddleware, createStore } from "redux";

const reducer = (initialState=0, action) => {

if (action.type === "INC") {

return initialState + 1;

} else if (action.type === "DEC") {

return initialState - 1;

} else if (action.type === "MULT") {

throw new Error("AHHHH!!");

}

return initialState;

}

-------------------------------------------------------------

const logger = (store) => (next) => (action) => {

console.log("Logged", action);

return next(action);

};

const errorHandler = (store) => (next) => (action) => {

try {

return next(action);

} catch(e) {

console.log("ERROR!", e);

}

};

const middleware= applyMiddleware(

logger,

errorHandler

)

-------------------------------------------------------------

const store = createStore(reducer, middleware)

store.subscribe(() => {

console.log("store changed", store.getState());

})

store.dispatch({type: "INC"})

store.dispatch({type: "INC"})

store.dispatch({type: "INC"})

store.dispatch({type: "DEC"})

store.dispatch({type: "DEC"})

store.dispatch({type: "DEC"})

store.dispatch({type: "MULT"})

store.dispatch({type: "DEC"})

  • redux-logger 使用

 

  

异步操作实现


一、用户送出第一个 Action

Ref: RUAN的博文可能更容易理解些

如果发送的信号(action)涉及到服务端,那么异步就是不可避免的事情。

  • 整个异步操作的思路:

第一个action:操作开始时,送出一个 Action,触发 State 更新为"正在操作"状态,View 重新渲染

第二个action:操作结束后,再送出一个 Action,触发 State 更新为"操作结束"状态,View 再一次重新渲

  • 异步需要三种action,三种 Action 可以有两种不同的写法:

// 写法一:名称相同,参数不同

{ type: 'FETCH_POSTS' }

{ type: 'FETCH_POSTS', status: 'error', error: 'Oops' }

{ type: 'FETCH_POSTS', status: 'success', response: { ... } }

// 写法二:名称不同

{ type: 'FETCH_POSTS_REQUEST' }

{ type: 'FETCH_POSTS_FAILURE', error: 'Oops' }

{ type: 'FETCH_POSTS_SUCCESS', response: { ... } }

  • State 也要进行改造,反映不同的操作状态:

let state = {

// ...

isFetching: true,      // 表示是否在抓取数据

didInvalidate: true,    // 表示数据是否过时

lastUpdated: 'xxxxxxx'   // 表示上一次更新时间

};

二、自动送出第二个 Action

  • 代码背景

异步操作至少要送出两个 Action:

    1. 用户触发第一个 Action,这个跟同步操作一样,没有问题;
    2. 如何才能在操作结束时,系统自动送出第二个 Action 呢?

# 异步组件的例子

class AsyncApp extends Component {
componentDidMount() {

const { dispatch, selectedPost } = this.props
dispatch(fetchPosts(selectedPost))  // fetchPosts是送给server的信号(action)
}

// ... 

 

  • Step 1 - 返回的是一个函数,而非”对象”

What kind of ActionCreator it is?  

 (1) 先发出一个Action(requestPosts(postTitle)) ----> 然后进行异步操作。

 (2) 拿到结果后,先将结果转成 JSON 格式 ----> 然后再发出一个 Action( receivePosts(postTitle, json))。

const fetchPosts = postTitle => (dispatch, getState) => {

dispatch(requestPosts(postTitle));

return fetch(`/some/API/${postTitle}.json`)

.then(response => response.json())

.then(json => dispatch(receivePosts(postTitle, json)));

};

};

  • Step 2 - 如何使用

// 使用方法一

store.dispatch( fetchPosts('reactjs') );
// 使用方法二

store.dispatch( fetchPosts('reactjs') ).then(() =>

console.log(store.getState())

);

返回的函数的参数:dispatchgetState这两个 Redux 方法,而非 Action 的内容。

 

  • 注意事项

(1)fetchPosts 返回了一个函数,而普通的 Action Creator 默认返回一个对象。

(2)返回的函数的参数是dispatchgetState这两个 Redux 方法,普通的 Action Creator 的参数是 Action 的内容。

(3)在返回的函数之中,先发出一个 Action(requestPosts(postTitle)),表示操作开始。

(4)异步操作结束之后,再发出一个 Action(receivePosts(postTitle, json)),表示操作结束。

 

 

三、中间件帮助"参数扩展"

  • redux-thunk:方案一,使参数支持 “函数"

通过中间件redux-thunk,改造store.dispatch,使得后者可以接受函数作为参数。

import { createStore, applyMiddleware } from 'redux';

import thunk from 'redux-thunk';

import reducer from './reducers';

// Note: this API requires redux@>=3.1.0

const store = createStore(    // 其实就是返回了一个特殊的store,是store.dispatch支持函数作为参数了

reducer,

applyMiddleware(thunk)

);

 

  • redux-promise:方案二,使参数支持 “Promise对象”

让 Action Creator 返回一个 Promise 对象,乃另一种异步操作的解决方案 through 使用redux-promise中间件。

import { createStore, applyMiddleware } from 'redux';

import promiseMiddleware from 'redux-promise';

import reducer from './reducers';

const store = createStore(

reducer,

applyMiddleware(promiseMiddleware)

);

详情请见:[JS] ECMAScript 6 - Async : compare with c#

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。

接受 Promise 对象作为参数,有两种写法:

** 写法一,返回值是一个 Promise 对象。

const fetchPosts = 

(dispatch, postTitle) => new Promise(function (resolve, reject) {

dispatch(requestPosts(postTitle));

return fetch(`/some/API/${postTitle}.json`)

.then(response => {

type: 'FETCH_POSTS',

payload: response.json()

});

});

** 写法二,Action 对象的payload属性是一个 Promise 对象。

import { createAction } from 'redux-actions';

class AsyncApp extends Component {

componentDidMount() {

const { dispatch, selectedPost } = this.props
// 发出同步 Action

dispatch(requestPosts(selectedPost));
// 发出异步 Action, 只有等到操作结束,这个 Action 才会实际发出;
// 注意,createAction的第二个参数必须是一个 Promise 对象。
dispatch(createAction(

'FETCH_POSTS',            // 第一个参数

fetch(`/some/API/${postTitle}.json`)    // 第二个参数

.then(response => response.json())

));

}

 

代码举例子:payload属性是一个 Promise 对象

 ----> Without promise, 发送完信号后,还要听过axios.then...catch...定义”返回状态处理“函数。

 ----> With promise, 如下使用axios (基于promise的http库)。

import { applyMiddleware, createStore } from "redux";

import axios from "axios";

import logger from "redux-logger";

import thunk from "redux-thunk";

import promise from "redux-promise-middleware";

const initialState = {

fetching: false,

fetched: false,

users: [],

error: null,

};


// 改变state的部分value

const reducer = (state=initialState, action) => {

switch (action.type) {
/**
* 因为使用了promise,所以默认使用promise的性质
* 其中包括了promise的三个状态定义:pending, rejected, fulfilled
*/

case "FETCH_USERS_PENDING": {

return {...state, fetching: true}

break;

}

case "FETCH_USERS_REJECTED": {

return {...state, fetching: false, error: action.payload}

break;

}

case "FETCH_USERS_FULFILLED": {

return {

...state,

fetching: false,

fetched: true,

users: action.payload,

}

break;

}

}

return state

}

const middleware = applyMiddleware(promise(), thunk, logger())

const store = createStore(reducer, middleware)


// 因为promise,这里就省去了

store.dispatch({

type: "FETCH_USERS",  # 自定发送”添加promose默认后缀“后的信号(action)

payload: axios.get("http://rest.learncode.academy/api/wstern/users")

})

以上是 [React] 12 - Redux: async & middleware 的全部内容, 来源链接: utcz.com/z/382660.html

回到顶部