redux引用多个中间件_如何轻松创建您的第一个Redux中间件

redux引用多个中间件

by Gabriele Cimato

加布里埃莱·西马托(Gabriele Cimato)

如何轻松创建您的第一个Redux中间件 (How to create your first Redux middleware with ease)

Almost every real-word React app makes extensive use of async requests. If you manage your app state with Redux, there are several ways to handle async actions.

几乎每个实词React应用程序都大量使用异步请求。 如果您使用Redux管理应用程序状态,则有几种方法可以处理异步操作。

You may have heard of redux-thunkor redux-saga, the most popular solutions to handling async actions in Redux. Such approaches come in handy when you need to track the status of a request in your state.

您可能听说过redux-thunkredux-saga ,这是在Redux中处理异步操作的最流行解决方案。 当您需要跟踪状态中的请求状态时,此类方法会派上用场。

A pattern that I have seen quite often that leverages thunks is the following:

我经常看到的一种利用重thunks的模式如下:

import {
  FETCH_DATA_ERROR,
  FETCH_DATA_PENDING,
  FETCH_DATA_SUCCESS,
} from 'constants/actionTypes';

function fetchMyDataError(error) {
  return {
    type: FETCH_DATA_ERROR,
    payload: error,
  };
}

function fetchDataPending() {
  return { type: FETCH_DATA_PENDING };
}

function fetchMyDataSuccess(response) {
  return {
    type: FETCH_DATA_SUCCESS.
    payload: response,
  };
}

function fetchData() {
  return (dispatch) => {
    dispatch(fetchDataPending());
      
    fetch('https://my-api.com/my-data')
      .then(res => res.json())
      .then(data => dispatch(fetchMyDataSuccess(data)))
      .catch(err => dispatch(fetchMyDataError(err)));
  };
}

As you can see, we wrote a good amount of code. This example can be simplified and handled with one function only. Either way, it will soon feel very repetitive and tedious, especially if you need to track the lifespan of every async request in your app. Such verbosity doesn’t help with the boilerplate necessary for an app that uses Redux.

如您所见,我们编写了很多代码。 此示例只能通过一个功能进行简化和处理。 无论哪种方式,它都会很快变得重复且乏味,特别是如果您需要跟踪应用程序中每个异步请求的寿命。 这种冗长的语言对于使用Redux的应用程序没有必要的样板。

When a pattern or a code block gets used over and over again, it’s a good practice to extract it in a function. This will abstract the logic of it, and only requires the least amount of data to “function.” That’s when I started playing with the idea of writing my own middleware. redux-slim-async helps me skip boilerplate code and provide great control with a tiny API. Let’s see now the previous example with the new middleware:

当一遍又一遍地使用模式或代码块时,将其提取到函数中是一种好习惯。 这将抽象它的逻辑,并且只需要最少的数据量即可“起作用”。 从那时起,我开始尝试编写自己的中间件。 redux-slim-async帮助我跳过样板代码,并通过一个微小的API提供了很好的控制。 现在让我们看一下带有新中间件的上一个示例:

import {
  FETCH_DATA_PENDING,
  FETCH_DATA_SUCCESS,
  FETCH_DATA_ERROR,
} from 'constants/actionTypes';

function fetchData() {
  return {
    types: [
      FETCH_DATA_PENDING,
      FETCH_DATA_SUCCESS,
      FETCH_DATA_ERROR,
    ],
    callAPI: fetch(‘https://my-api.com/my-data')
      .then(res => res.json()),
  }
}

All those awkward functions are gone and our fetchData is now minimal — pretty neat! ?

所有这些笨拙的功能都消失了,现在我们的fetchData变得很小了-非常整洁! ?

Now let’s go ahead and build a smaller version of such middleware. It will help us understand the inner workings of it and hey, you’ll be able to build your own next!

现在,让我们继续构建这种中间件的较小版本。 它将帮助我们了解其内部工作原理,嘿,您将能够构建自己的下一个!

创建中间件 (Creating a middleware)

Let me show you the code for this small middleware right away. You’ll see that it’s not as overwhelming as you might think.

让我立即向您展示此小型中间件的代码。 您会发现它并不像您想像的那样令人不知所措。

function createSlimAsyncMiddleware({ dispatch, getState }) {
  return next => action => {
    const {
      types,
      callAPI,
      shouldCallAPI = () => true,
    } = action;
      
    if (!actionIsValid(action)) next(action);
    if (!shouldCallAPI(getState())) {
      return Promise.resolve(getState());
    }
      
    const [pendingType, successType, errorType] = types;
      
    dispatch({ type: pendingType });
      
    return callAPI()
      .then(response => {
        dispatch({
          type: successType,
          payload: response,
        });
        
        return Promise.resolve(getState());
      })
      .catch(error => {
        dispatch({
          type: errorType,
          payload: error,
        });
        
        return Promise.reject(error);
     });
  };
}

Wait a second…that’s it? Absolutely!

等一下...是吗? 绝对!

Let’s go one line at a time. This middleware is a function that returns a function, that returns a function that returns a Promise. As funky as it sounds, you’ll find that it’s much simpler than it seems.

我们一次去一行。 该中间件是一个返回函数的函数,该函数返回一个函数,该函数返回一个Promise 。 听起来很时髦,但您会发现它比看起来简单得多。

Our middleware function receives an object with two fields: dispatch and getState. These are named parameters provided by Redux.

我们的中间件函数接收具有两个字段的对象: dispatchgetState 。 这些是Redux提供的命名参数

  • dispatch: as the name suggests, this is what we use to dispatch an action. It’ll give us the power of handling actions inside the middleware

    dispatch :顾名思义,这就是我们用来派发动作的方式。 它使我们能够处理中间件中的动作

  • getState: this is a function that returns the current state at a given time. This can be useful if we want to return the updated state after an action has been dispatched

    getState :这是一个在给定时间返回当前状态的函数。 如果我们要在分派动作后返回更新的状态,这将很有用

On the first line we have a function with one object argument with fields dispatch and getState.

第一行,我们有一个带有一个对象参数的函数,该函数带有字段dispatchgetState

On the second line we return a function that takes an argument called next. Such a function returns a function that takes an action and does something. More on that later. But what is next for ? Why are we expected to return a function that returns a function that does something?

第二行中,我们返回一个接受名为next的参数的函数。 这样的函数返回执行action并执行某些action的函数。 以后再说。 但是,什么是next呢? 为什么我们期望返回一个函数,该函数返回执行某些操作的函数?

What Redux does under the hood is compose the middlewares so that each one has a reference to…the next one! The name helps a lot to make it intuitive. We are wrapping the official Redux dispatch function with our middleware. This builds a pipeline that an action has to go through.

Redux在幕后所做的就是构成中间件,这样每个中间件都可以参考…… next一个中间件! 该名称对使其直观很有帮助。 我们使用中间件包装了官方的Redux dispatch功能。 这建立了一个动作必须经过的管道。

Remember that you don’t HAVE TO call next(action), but you need to do so if you don’t want to block the dispatching process (we’ll see a specific case in our middleware).

请记住,您不必调用next(action) ,但是如果您不想阻塞调度过程,则需要这样做(我们将在中间件中看到一个特定的情况)。

In our case, it’s useful because we don’t want to intercept every action, only the ones that are valid for our middleware. For simplicity, I added a check called actionIsValid. This function takes an action as an argument and returns a boolean. The returned boolean represents the validity of this action for our middleware.

在我们的例子中,它很有用,因为我们不想拦截每个动作,而只希望拦截对中间件有效的动作。 为简单起见,我添加了一个名为actionIsValid的检查。 此函数将action作为参数并返回布尔值。 返回的布尔值表示此操作对我们的中间件的有效性。

actionisValid is a good place to check for errors and throw them if necessary. If it’s not valid, then I will use our reference to the next middleware and pass the action to it. Otherwise we can finally use the action and “do something” (the flowchart above represents a simplified version of this logic).

actionisValid是检查错误并在必要时throwthrow的好地方。 如果无效,那么我将使用对next中间件的引用,并将操作传递给它。 否则,我们最终可以使用该操作并“执行某些操作”(上面的流程图表示此逻辑的简化版本)。

The rest of the middleware is pretty intuitive. We check the validity of the action to determine if our async request should proceed or not.

中间件的其余部分非常直观。 我们检查操作的有效性,以确定我们的异步请求是否应该继续进行。

shouldCallAPI is a parameter of our middleware API. Given the state, it returns a boolean that determines if our request should execute or not. The middleware provides a default value for it (a function that returns true ). If we don’t need to make the API call, then we return Promise.resolve. This way we can use .then or async/await on any asynchronous action that goes through our middleware.

shouldCallAPI是我们的中间件API的参数。 给定状态,它返回一个布尔值,该布尔值确定我们的请求是否应该执行。 中间件为其提供默认值(返回true的函数)。 如果我们不需要进行API调用,则返回Promise.resolve 。 这样,我们可以对通过中间件的任何异步操作使用.thenasync/await

const [pendingType, successType, errorType] = types;

The next step is to determine the action type field passed in as a parameter. We use array destructuring to disassemble our types array parameter.

下一步是确定作为参数传入的操作type字段。 我们使用数组解构来分解types数组参数。

dispatch({ type: pendingType });

Now we can finally use the dispatch method. This dispatches a Redux action like you would normally do. Such action represents the “pending” state of our async request.

现在我们终于可以使用dispatch方法了。 这将像通常那样调度Redux动作。 此类操作表示异步请求的“待处理”状态。

return callAPI()
  .then(response => {
    dispatch({
      type: successType,
      payload: response,
    });
    
    return Promise.resolve(getState());
  })
  .catch(error => {
    dispatch({
      type: errorType,
      payload: error,
    });
    
    return Promise.reject(error);
  });

We finally have our last return statement. Here we make the API call and, based on how the Promise resolves, we dispatch and return different values.

我们终于有了最后的return声明。 在这里,我们进行API调用,并根据Promise解析方式,调度并返回不同的值。

  • Success: given the response from the API, we dispatch a success action. The payload is the response of the request. Right after that, we return a Promise that resolves with the up-to-date state of our app. This allows us to use .then(updatedState => …do something)

    成功 :根据API的响应,我们将调度成功操作。 有效负载是请求的响应。 此后,我们将返回一个Promise ,该Promise将使用我们应用程序的最新状态进行解析。 这使我们可以使用.then(updatedState => …do somethi )

  • Error: if the Promise rejects then we dispatch an error action. In this case the payload is the error itself.

    错误:如果Promise拒绝,则我们将调度错误操作。 在这种情况下,有效载荷本身就是错误。

That’s it! As shown before, we can then create actions and use them as follows:

而已! 如前所示,我们可以创建动作并按如下方式使用它们:

// Our Action

function fetchData() {
  return {
    types: [
      FETCH_DATA_PENDING,
      FETCH_DATA_SUCCESS,
      FETCH_DATA_ERROR,
    ],
    shouldCallAPI: state => state.dataArr.length === 0,
    callAPI: () =>
      fetch('https://my-api.com/my-data').then(res => res.json()),
  }
}

// Inside the component

class MyComponent extends Component {
  componentDidMoun() {
    this.props.fetchData()
      .then(state => {
        console.log('updated state after async action:', state);
      })
      .catch(err => {
        console.log('an error occured');
      });
  }
  
// Rest of the component omitted...

}

In this simple case we fetch data only if our data array is empty. Then we log the updated state after the request or an error message if the Promise rejects..

在这种简单情况下,我们仅在数据数组为空时才获取数据。 然后,我们在请求后记录更新后的状态,如果Promise拒绝,则会记录一条错误消息。

结论 (Conclusion)

Creating Redux middlewares is intuitive. You have access to the store dispatcher and the getState function. Use them to access the latest state of your app or dispatch actions.

创建Redux中间件很直观。 您可以访问商店调度程序和getState函数。 使用它们来访问您的应用程序的最新状态或调度操作。

You also need to remember to use next when necessary and make sure not to block the dispatching pipeline. In our case, if we didn’t call next(action) , any action that was not valid for our middleware would be basically discarded ⚠️!!

您还需要记住在必要时使用next ,并确保不要阻塞分派管道。 在我们的情况下,如果我们不调用next(action) ,那么对于中间件无效的任何动作都将被丢弃⚠️!!

Some implementation details were omitted here for simplicity. If you want to dig a little deeper, feel free to explore the redux-slim-async middleware here.

为了简化起见,这里省略了一些实现细节。 如果您想更深入一点,请随时在此处探索redux-slim-async中间件。

Give it a ⭐️ if you like it! I built this middleware and currently use it in production to avoid a lot of boilerplate. Feel free to give it a try and provide feedback anytime. Here is another valuable resource to explore middlewares even more, the redux docs!

如果喜欢,给它一个⭐️! 我构建了这种中间件,目前在生产中使用它来避免大量重复样板。 随时尝试一下,随时提供反馈。 redux docs是另一个可进一步探索中间件的宝贵资源!

You can also follow me on twitter @SuperGabry

您也可以在Twitter上关注我@SuperGabry

翻译自: https://www.freecodecamp.org/news/how-to-create-your-first-redux-middleware-with-ease-a75e6b1384db/

redux引用多个中间件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值