本文转载自:众成翻译
译者:iOSDevLog
连接:http://www.zcfy.cc/article/3810
原文:https://www.fullstackreact.com/30-days-of-react/day-21/react
今天,咱们在Redux方法中使用Redux中间件来管理咱们的代码中的复杂状态变化。json
昨天, 咱们链接的点与Redux, 从工做经过归并器, 更新行动的创造者, 并链接Redux到React组件。 Redux中间件 将解锁更多的权力, 咱们今天将会触及。redux
中间件一般指的是软件服务, "粘合在一块儿" 在现有软件中的独立功能。对于Redux, 中间件提供了一个 第三方扩展点, 在分发动做和将分发交给归并器之间:api
[ Action ] [ Middleware ] [ Dispatcher ]promise
[ 动做 ] [ 中间件 ] [ 分发 ]浏览器
中间件的示例包括日志记录、崩溃报告、路由、处理异步请求等。缓存
让咱们来处理异步请求, 就像对服务器的 HTTP 调用那样。中间件是一个很好的地方。服务器
咱们将实现一些中间件, 它将表明咱们处理异步请求。app
中间件位于动做和归并器之间。它能够监听全部的调度和执行代码与行动和当前状态的细节。中间件提供了一个强大的抽象。让咱们来看看如何使用它来管理咱们本身的。异步
继续咱们从昨天开始的currentTime
Redux的工做, 让咱们构建咱们的中间件, 以获取当前的时间从服务器, 咱们用几天前写的真实从 API 服务获取时间。
在咱们作得太多以前, 让咱们从reducers.js
文件的rootReducer
中取出currentTime
的放到它本身的文件。咱们离开了根归并器在一个状态, 咱们保持 currentTime
工做在根归并器。一般来讲, 咱们将这些文件移动到他们本身的文档中, 并使用rootReducer.js
文件 (咱们称之为reducers.js
) 来保持主组合归并器。
First, let's pull the work into it's own file in redux/currentTime.js
. We'll export two objects from here (and each reducer):首先, 让咱们把工做归入到它本身的redux/currentTime.js
文件。咱们将从这里 (和每一个归并器) 导出两个对象:
initialState
- 状态树的这个分支的初始状态
reducer
-这个分支的归并器
import * as types from './types'; export const initialState = { currentTime: new Date().toString(), } export const reducer = (state = initialState, action) => { switch(action.type) { case types.FETCH_NEW_TIME: return { ...state, currentTime: action.payload} default: return state; } } export default reducer
根归并器用咱们的currentTime
, 咱们将须要更新reducers.js
文件接受新文件到根归并器。幸运的是, 这很简单:
import { combineReducers } from 'redux'; import * as currentUser from './currentUser'; import * as currentTime from './currentTime'; export const rootReducer = combineReducers({ currentTime: currentTime.reducer, currentUser: currentUser.reducer, }) export const initialState = { currentTime: currentTime.initialState, currentUser: currentUser.initialState, } export default rootReducer
最后, 让咱们更新configureStore
函数, 从文件中提取 rootReducer 和初始状态:
import { rootReducer, initialState } from './reducers' // ... export const configureStore = () => { const store = createStore( rootReducer, initialState, ); return store; }
中间件基本上是一个接受store
函数, 它将返回一个接受next
函数, 这将返回一个接受动做的函数。有点乱?让咱们看看这意味着什么。
让咱们构建最小的中间件, 咱们可能可以准确地理解到底发生了什么, 以及如何将它添加到咱们的栈中。
让咱们建立咱们的第一个中间件。
如今, 中间件的签名看起来像这样:
const loggingMiddleware = (store) => (next) => (action) => { // Our middleware }
对这个中间件的事情很迷惑?别担忧, 咱们都是第一次看到它。让咱们把它剥离回来一点点, 拆解发生了什么事。上面的loggingMiddleware
描述能够像下面这样重写:
const loggingMiddleware = function(store) { // Called when calling applyMiddleware so // our middleware can have access to the store return function(next) { // next is the following action to be run // after this middleware return function(action) { // finally, this is where our logic lives for // our middleware. } } }
咱们不须要担忧 怎么 被调用, 只是它确实获得了这个顺序调用。让咱们加强咱们的loggingMiddleware
, 这样咱们实际上就能够注销被调用的动做:
const loggingMiddleware = (store) => (next) => (action) => { // Our middleware console.log(`Redux Log:`, action) // call the next function next(action); }
Our middleware causes our store to, when every time an action is called, we'll get a console.log
with the details of the action.咱们的中间件致使咱们的存储被调用,咱们会获得一个console.log
动做细节。
为了将中间件应用到咱们的栈中, 咱们将用这个恰当命名的applyMiddleware
函数做为 createStore()
方法的第三个参数。
import { createStore, applyMiddleware } from 'redux';
对于 应用 中间件, 咱们能够在 createStore()
方法中调用这个 applyMiddleware()
函数。在咱们的 src/redux/configureStore.js
文件中, 让咱们经过添加对applyMiddleware()
的调用来更新存储建立:
const store = createStore( rootReducer, initialState, applyMiddleware( apiMiddleware, loggingMiddleware, ) );
如今咱们的中间件已经到位。在浏览器中打开控制台以查看此演示所调用的全部动做。尝试单击打开控制台的Update
按钮.。
正如咱们所看到的, 中间件使咱们可以在咱们的Redux动做调用链中插入一个函数。在该函数中, 咱们能够访问该动做、状态, 并且咱们还可以分发其余动做。
咱们但愿编写一个能够处理 API 请求的中间件函数。咱们能够编写一个中间件函数, 它只侦听与 API 请求对应的动做。咱们的中间件能够 "监视" 具备特殊标记的动做。例如, 咱们能够有一个 meta
对象的行动与 type
的 'api'
。咱们可使用它来确保咱们的中间件不处理与 API 请求无关的任何动做:
const apiMiddleware = store => next => action => { if (!action.meta || action.meta.type !== 'api') { return next(action); } // This is an api request }
若是某个动做有一个带有 'api'
,类型的元对象, 咱们将在 apiMiddleware
.中接收该请求。
让咱们转换咱们的updateTime()
actionCreator, 将这些属性包含到一个 API 请求中。让咱们打开咱们一直在使用的currentTime
Redux模块 (在src/redux/currentTime.js
), 并找到fetchNewTime()
函数定义。
让咱们把这个请求的 URL 传递给咱们的meta
对象。咱们甚至能够从调用动做建立者的内部接受参数:
const host = 'https://andthetimeis.com' export const fetchNewTime = ({ timezone = 'pst', str='now'}) => ({ type: types.FETCH_NEW_TIME, payload: new Date().toString(), meta: { type: 'api', url: host + '/' + timezone + '/' + str + '.json' } })
当咱们按下按钮更新的时间, 咱们的apiMiddleware
将结束了在归并器以前截取。对于咱们在中间件中捕获的任何调用, 咱们能够将元对象拆分, 并使用这些选项进行请求。或者, 咱们能够经过fetch()
API 将整个被消毒的meta
对象传递出去。
咱们的 API 中间件须要采起的步骤:
从 meta 中查找请求 URL 并撰写请求选项
提出要求
将请求转换为 JavaScript 对象
回复Redux/用户
让咱们采起这按步就班的步骤。首先, 关闭 URL
并建立fetchOptions
以传递到fetch()
。咱们将在下面的代码中的注释中列出这些步骤:
const apiMiddleware = store => next => action => { if (!action.meta || action.meta.type !== 'api') { return next(action); } // This is an api request // Find the request URL and compose request options from meta const {url} = action.meta; const fetchOptions = Object.assign({}, action.meta); // Make the request fetch(url, fetchOptions) // convert the response to json .then(resp => resp.json()) .then(json => { // respond back to the user // by dispatching the original action without // the meta object let newAction = Object.assign({}, action, { payload: json.dateString }); delete newAction.meta; store.dispatch(newAction); }) } export default apiMiddleware
咱们有几个选项, 咱们如何回复到Redux链中的用户。就我的而言, 咱们更喜欢用相同的类型响应请求被激发, 而没有 meta
标记, 并将响应体做为新动做的 payload 有效负载
。
这样, 咱们不须要改变咱们的Redux归并器来管理响应任何不一样的, 若是咱们没有提出要求。
咱们也不限于一个单一的响应。假设咱们的用户在请求完成时经过了onSuccess
回调来调用。咱们能够调用这个onSuccess
回调, 而后发送备份链:
const apiMiddleware = store => next => action => { if (!action.meta || action.meta.type !== 'api') { return next(action); } // This is an api request // Find the request URL and compose request options from meta const {url} = action.meta; const fetchOptions = Object.assign({}, action.meta); // Make the request fetch(url, fetchOptions) // convert the response to json .then(resp => resp.json()) .then(json => { if (typeof action.meta.onSuccess === 'function') { action.meta.onSuccess(json); } return json; // For the next promise in the chain }) .then(json => { // respond back to the user // by dispatching the original action without // the meta object let newAction = Object.assign({}, action, { payload: json.dateString }); delete newAction.meta; store.dispatch(newAction); }) }
这里的可能性几乎是无止境的。让咱们添加apiMiddleware
到咱们的链经过它更新configureStore()
函数:
import { createStore, applyMiddleware } from 'redux'; import { rootReducer, initialState } from './reducers' import loggingMiddleware from './loggingMiddleware'; import apiMiddleware from './apiMiddleware'; export const configureStore = () => { const store = createStore( rootReducer, initialState, applyMiddleware( apiMiddleware, loggingMiddleware, ) ); return store; } export default configureStore;
请注意, 咱们没必要更改视图的 _任意_代码 以更新数据在状态树中的填充方式。很漂亮吧?
这个中间件很是简单, 但它是构建它的良好基础。您是否能够考虑如何实现缓存服务, 以便咱们不须要对已有的数据进行请求?如何让一个跟踪挂起的请求, 这样咱们就能够为未完成的请求显示一个微调框?
太棒了!如今咱们真的是Redux忍者。咱们已经征服了Redux大山, 并准备继续下一步的行动。在咱们去以前, 可是..。咱们已经完成了3周!