Ref: Redux 入门教程(二):中间件与异步操做html
这里只是简单地了解中间件的概念,对于异步,貌似以后要讲的saga更胜一筹。react
reducer计算新状态的策略:ios
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作了新的定义:json
--- 不只给store发送信号action。redux
--- 并且附带了log的功能。axios
let next = store.dispatch;
store.dispatch = function dispatchAndLog(action) {
console.log('dispatching', action);
next(action);
console.log('next state', store.getState());
}
再理解以下文字:c#
(1)Reducer:纯函数,只承担计算 State 的功能,不合适承担其余功能,也承担不了,由于理论上,纯函数不能进行读写操做。api
(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的用法。
store.dispatch ==> middleware --> logger ==> "action fired."
其实就是一个多层嵌套返回函数的函数,
柯里化 - 使用箭头的写法在函数式编程,对柯里化更详细的介绍能够看一看这篇 张鑫旭的博客。
第一个(最外层)方法的参数是一个包含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"})
Ref: RUAN的博文可能更容易理解些
若是发送的信号(action)涉及到服务端,那么异步就是不可避免的事情。
第一个action:操做开始时,送出一个 Action,触发 State 更新为"正在操做"状态,View 从新渲染
第二个action:操做结束后,再送出一个 Action,触发 State 更新为"操做结束"状态,View 再一次从新渲
// 写法一:名称相同,参数不一样
{ 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: { ... } }
let state = {
// ...
isFetching: true, // 表示是否在抓取数据
didInvalidate: true, // 表示数据是否过期
lastUpdated: 'xxxxxxx' // 表示上一次更新时间
};
异步操做至少要送出两个 Action:
# 异步组件的例子
class AsyncApp extends Component {
componentDidMount() { const { dispatch, selectedPost } = this.props
dispatch(fetchPosts(selectedPost)) // fetchPosts是送给server的信号(action)
} // ...
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)));
};
};
// 使用方法一
store.dispatch( fetchPosts('reactjs') );
// 使用方法二
store.dispatch( fetchPosts('reactjs') ).then(() =>
console.log(store.getState())
);
返回的函数的参数:dispatch
和getState
这两个 Redux 方法,而非 Action 的内容。
(1)fetchPosts
返回了一个函数,而普通的 Action Creator 默认返回一个对象。
(2)返回的函数的参数是dispatch
和getState
这两个 Redux 方法,普通的 Action Creator 的参数是 Action 的内容。
(3)在返回的函数之中,先发出一个 Action(requestPosts(postTitle)
),表示操做开始。
(4)异步操做结束以后,再发出一个 Action(receivePosts(postTitle, json)
),表示操做结束。
经过中间件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)
);
让 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 才会实际发出;
// 注意,的第二个参数必须是一个 Promise 对象。
dispatch(createAction(
'FETCH_POSTS', // 第一个参数
fetch(`/some/API/${postTitle}.json`) // 第二个参数
.then(response => response.json())
));
}createAction
代码举例子: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") })