React学习笔记_redux二
来源:互联网 发布:学汉语软件 编辑:程序博客网 时间:2024/06/06 08:44
一、 Redux 中间件与异步操作
Action 发出以后,Reducer 立即算出 State,这叫做同步;Action 发出以后,过一段时间再执行 Reducer,这就是异步。 使用中间件(middleware)达到异步
一、中间件的概念
- Reducer:纯函数,只承担计算 State 的功能,不合适承担其他功能,也承担不了,因为理论上,纯函数不能进行读写操作。
- View:与 State 一一对应,可以看作 State 的视觉层,也不合适承担其他功能。
- Action:存放数据的对象,即消息的载体,只能被别人操作,自己不能进行任何操作。
对store.dispatch进行如下改造。
let next = store.dispatch;store.dispatch = function dispatchAndLog(action) { console.log('dispatching', action); next(action); console.log('next state', store.getState());}
上面代码中,对store.dispatch进行了重定义,在发送 Action 前后添加了打印功能。这就是中间件的雏形。 中间件就是一个函数,对store.dispatch方法进行了改造,在发出 Action 和执行 Reducer 这两步之间,添加了其他功能。
二、中间件的用法
redux-logger
import { applyMiddleware, createStore } from 'redux';import createLogger from 'redux-logger';const logger = createLogger();const store = createStore( reducer, applyMiddleware(logger));
上面代码中,redux-logger提供一个生成器createLogger,可以生成日志中间件logger。然后,将它放在applyMiddleware方法之中,传入createStore方法,就完成了store.dispatch()的功能增强。
这里有两点需要注意:
- createStore方法可以接受整个应用的初始状态作为参数,那样的话,applyMiddleware就是第三个参数了。
const store = createStore( reducer, initial_state, applyMiddleware(logger));
- 中间件的次序有讲究。
const store = createStore( reducer, applyMiddleware(thunk, promise, logger));
上面代码中,applyMiddleware方法的三个参数,就是三个中间件。有的中间件有次序要求,使用前要查一下文档。比如,logger就一定要放在最后,否则输出结果会不正确。
三、applyMiddlewares()
看到这里,你可能会问,applyMiddlewares这个方法到底是干什么的? 它是 Redux 的原生方法,作用是将所有中间件组成一个数组,依次执行。下面是它的源码。
export default function applyMiddleware(...middlewares) { return (createStore) => (reducer, preloadedState, enhancer) => { var store = createStore(reducer, preloadedState, enhancer); var dispatch = store.dispatch; var chain = []; var middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) }; chain = middlewares.map(middleware => middleware(middlewareAPI)); dispatch = compose(...chain)(store.dispatch); return {...store, dispatch} }}
上面代码中,所有中间件被放进了一个数组chain,然后嵌套执行,最后执行store.dispatch。可以看到,中间件内部(middlewareAPI)可以拿到getState和dispatch这两个方法。
四、 异步操作的基本思路
理解了中间件以后,就可以处理异步操作了。 同步操作只要发出一种 Action 即可,异步操作的差别是它要发出三种 Action。
- 操作发起时的 Action
- 操作成功时的 Action
- 操作失败时的 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: { ... } }
除了 Action 种类不同,异步操作的 State 也要进行改造,反映不同的操作状态。下面是 State 的一个例子。
let state = { // ... isFetching: true, didInvalidate: true, lastUpdated: 'xxxxxxx'};
上面代码中,State 的属性isFetching表示是否在抓取数据。didInvalidate表示数据是否过时,lastUpdated表示上一次更新时间。 现在,整个异步操作的思路就很清楚了。
操作开始时,送出一个 Action,触发 State 更新为"正在操作"状态,View 重新渲染操作结束后,再送出一个 Action,触发 State 更新为"操作结束"状态,View 再一次重新渲染
五 redux-thunk 中间件
异步操作至少要送出两个 Action:用户触发第一个 Action,这个跟同步操作一样,没有问题;如何才能在操作结束时,系统自动送出第二个 Action 呢? 奥妙就在 Action Creator 之中。
class AsyncApp extends Component { componentDidMount() { const { dispatch, selectedPost } = this.props dispatch(fetchPosts(selectedPost)) }// ...
上面代码是一个异步组件的例子。加载成功后(componentDidMount方法),它送出了(dispatch方法)一个 Action,向服务器要求数据 fetchPosts(selectedSubreddit)。这里的fetchPosts就是 Action Creator。 下面就是fetchPosts的代码,关键之处就在里面。
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()));
上面代码中,fetchPosts是一个Action Creator(动作生成器),返回一个函数。这个函数执行后,先发出一个Action(requestPosts(postTitle)),然后进行异步操作。拿到结果后,先将结果转成 JSON 格式,然后再发出一个 Action( receivePosts(postTitle, json))。 上面代码中,有几个地方需要注意。
- fetchPosts返回了一个函数,而普通的 Action Creator 默认返回一个对象。
- 返回的函数的参数是dispatch和getState这两个 Redux 方法,普通的 Action Creator 的参数是 Action 的内容。
- 在返回的函数之中,先发出一个 Action(requestPosts(postTitle)),表示操作开始。
- 异步操作结束之后,再发出一个 Action(receivePosts(postTitle, json)),表示操作结束。
这样的处理,就解决了自动发送第二个 Action 的问题。但是,又带来了一个新的问题,Action 是由store.dispatch方法发送的。而store.dispatch方法正常情况下,参数只能是对象,不能是函数。 这时,就要使用中间件redux-thunk。
import { createStore, applyMiddleware } from 'redux';import thunk from 'redux-thunk';import reducer from './reducers';// Note: this API requires redux@>=3.1.0const store = createStore( reducer, applyMiddleware(thunk));
上面代码使用redux-thunk中间件,改造store.dispatch,使得后者可以接受函数作为参数。 因此,异步操作的第一种解决方案就是,写出一个返回函数的 Action Creator,然后使用redux-thunk中间件改造store.dispatch。
六、redux-promise 中间件
既然 Action Creator 可以返回函数,当然也可以返回其他值。另一种异步操作的解决方案,就是让 Action Creator 返回一个 Promise 对象。 这就需要使用redux-promise中间件。
import { createStore, applyMiddleware } from 'redux';import promiseMiddleware from 'redux-promise';import reducer from './reducers';const store = createStore( reducer, applyMiddleware(promiseMiddleware));
这个中间件使得store.dispatch方法可以接受 Promise 对象作为参数。这时,Action Creator 有两种写法。写法一,返回值是一个 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 对象。这需要从redux-actions模块引入createAction方法,并且写法也要变成下面这样。
import { createAction } from 'redux-actions';class AsyncApp extends Component { componentDidMount() { const { dispatch, selectedPost } = this.props // 发出同步 Action dispatch(requestPosts(selectedPost)); // 发出异步 Action dispatch(createAction( 'FETCH_POSTS', fetch(`/some/API/${postTitle}.json`) .then(response => response.json()) )); }
上面代码中,第二个dispatch方法发出的是异步 Action,只有等到操作结束,这个 Action 才会实际发出。注意,createAction的第二个参数必须是一个 Promise 对象。 看一下redux-promise的源码,就会明白它内部是怎么操作的。
export default function promiseMiddleware({ dispatch }) { return next => action => { if (!isFSA(action)) { return isPromise(action) ? action.then(dispatch) : next(action); } return isPromise(action.payload) ? action.payload.then( result => dispatch({ ...action, payload: result }), error => { dispatch({ ...action, payload: error, error: true }); return Promise.reject(error); } ) : next(action); };}
从上面代码可以看出,如果 Action 本身是一个 Promise,它 resolve 以后的值应该是一个 Action 对象,会被dispatch方法送出(action.then(dispatch)),但 reject 以后不会有任何动作;如果 Action 对象的payload属性是一个 Promise 对象,那么无论 resolve 和 reject,dispatch方法都会发出 Action。
- React学习笔记_redux二
- React学习笔记_Redux
- React学习笔记_Redux-Saga
- React学习笔记(二)
- React-native 学习笔记二
- React Native 学习笔记(二)
- react整理二阶段学习笔记
- React Native 学习笔记(二)
- react && redux 学习笔记(二)
- React-Native学习笔记(二)
- react项目学习笔记二(react-router)
- React Native学习笔记(二)JSX 语法学习
- React Native 学习笔记二十一(定时器学习)
- React Native 学习笔记二十一(导航器三方 插件学习)
- React Native学习笔记二之写个Hello World
- React学习学习笔记
- react-native学习(二)
- React-Native 学习(二)
- 卡尔曼滤波——迭代过程
- 欢迎使用CSDN-markdown编辑器
- Advanced Installer 数据库连接字段参数和返回参数
- erlang 读取utf-8 BOM 问题
- 内容提供器的使用
- React学习笔记_redux二
- 递归反序链表
- windows下 查看golang gc 信息!!
- 用批处理压缩文件
- kudu1.5.0新特性和修复Bug
- @RequestBody,@ResponseBody的用法 和理解
- 50个php技巧
- Java虚拟机第二章2.2 学习笔记
- PHP通过循环来检测四维数组中的值是否存在数组中