资讯专栏INFORMATION COLUMN

【全栈React】第21天: Redux中间件

Scott / 1193人阅读

摘要:本文转载自众成翻译译者链接原文今天,我们在方法中使用中间件来管理我们的代码中的复杂状态变化。中间件是一个很好的地方。我们中间件我们将实现一些中间件它将代表我们处理异步请求。中间件位于动作和归并器之间。让我们创建我们的第一个中间件。

本文转载自:众成翻译
译者:iOSDevLog
链接:http://www.zcfy.cc/article/3810
原文:https://www.fullstackreact.com/30-days-of-react/day-21/

今天,我们在Redux方法中使用Redux中间件来管理我们的代码中的复杂状态变化。

昨天, 我们连接的点与Redux, 从工作通过归并器, 更新行动的创造者, 并连接Redux到React组件。 Redux中间件 将解锁更多的权力, 我们今天将会触及。

Redux中间件

中间件通常指的是软件服务, "粘合在一起" 在现有软件中的独立功能。对于Redux, 中间件提供了一个 第三方扩展点, 在分发动作和将分发交给归并器之间:

[ Action ] [ Middleware ] [ Dispatcher ]

[ 动作 ] [ 中间件 ] [ 分发 ]

中间件的示例包括日志记录、崩溃报告、路由、处理异步请求等。

让我们来处理异步请求, 就像对服务器的 HTTP 调用那样。中间件是一个很好的地方。

我们中间件api

我们将实现一些中间件, 它将代表我们处理异步请求。

中间件位于动作和归并器之间。它可以监听所有的调度和执行代码与行动和当前状态的细节。中间件提供了一个强大的抽象。让我们来看看如何使用它来管理我们自己的。

继续我们从昨天开始的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周!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/87166.html

相关文章

  • 全栈ReactReact 30教程索引

    摘要:今天我们将讨论创建组件的最终方案,即无状态函数的纯组件。今天我们正在研究一种处理提出的复杂数据的方法,称为体系结构。第天部署介绍今天,我们将探讨部署我们的应用所涉及的不同部分,以便外界可以使用我们的应用。 本文转载自:众成翻译译者:iOSDevLog链接:http://www.zcfy.cc/article/3758原文:https://www.fullstackreact.com/3...

    appetizerio 评论0 收藏0
  • 全栈React18: Flux 简介

    摘要:在方法中处理数据有三不同的角色派发器储存视图层我们的组件的主要思想是有一个单一源储存他们只能通过触发更新。这些操作负责调用派发器可以订阅更改并相应地更新自己的数据。与不同不使用派发器而是使用纯函数来定义数据变异函数。 本文转载自:众成翻译译者:iOSDevLog链接:http://www.zcfy.cc/article/3812原文:https://www.fullstackreact...

    mtunique 评论0 收藏0
  • 全栈React20: Redux动作

    摘要:去营救有一种方法我们把我们的归约器分成多个归约器每个都只负责状态树的叶子。此外我们还学习了如何扩展以使用多个归约器和动作以及多个连接的组件。 本文转载自:众成翻译译者:iOSDevLog链接:http://www.zcfy.cc/article/3825原文:https://www.fullstackreact.com/30-days-of-react/day-20/ 使用Redux,...

    SimonMa 评论0 收藏0
  • 全栈React23: 实现测试

    摘要:包包含由团队提供的测试实用程序。将在一个名为的目录中自动查找整个树中的测试文件是的带有下划线。让我们为时间轴组件创建第一个测试。其中之一是命令。现在我们已经编写了第一个测试并确认了我们的设置我们将在明天开始测试我们的时间轴组件。 本文转载自:众成翻译译者:iOSDevLog链接:http://www.zcfy.cc/article/3807原文:https://www.fullstac...

    airborne007 评论0 收藏0
  • 全栈React19: 用Redux进行数据管理

    摘要:归约器函数负责返回应用当前全局状态的表示形式。当我们在存储上发送操作时将使用应用的当前状态和导致状态更新的操作来调用此归约器函数。回到我们的归约器我们可以检查的动作类型并采取适当的步骤创建下一个状态。我们将处理动作创造者中归约器的副作用。 本文转载自:众成翻译译者:iOSDevLog链接:http://www.zcfy.cc/article/3811原文:https://www.ful...

    glumes 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<