Home >Web Front-end >JS Tutorial >What is react middleware?

What is react middleware?

青灯夜游
青灯夜游Original
2020-11-26 17:16:015421browse

In react, middleware is a function. The store.dispatch method has been modified. Other functions have been added between the two steps of issuing Action and executing Reducer; commonly used middleware is available. , just reference the modules written by others.

What is react middleware?

1. The concept of middleware

In order to understand middleware, let us think from the perspective of the framework author Question: If you were to add functionality, where would you add it?

(1) Reducer: Pure function, which only performs the function of calculating State. It is not suitable for other functions and cannot be performed because theoretically, pure functions cannot perform read and write operations.

(2) View: corresponds to State one-to-one. It can be regarded as the visual layer of State and is not suitable for other functions.

(3) Action: The object that stores data, that is, the carrier of the message, can only be operated by others and cannot perform any operations by itself.

After much deliberation, only the step of sending Action, that is, the store.dispatch() method, can add functionality. For example, to add logging function and print out Action and State, you can modify store.dispatch as follows.

let next = store.dispatch;
store.dispatch = function dispatchAndLog(action) {
  console.log('dispatching', action);
  next(action);
  console.log('next state', store.getState());
}

In the above code, store.dispatch is redefined and the printing function is added before and after sending the Action. This is the prototype of middleware.

Middleware is a function that modifies the store.dispatch method and adds other functions between the two steps of issuing Action and executing Reducer. Commonly used middleware is ready-made, just refer to the modules written by others.

2. Usage of middleware

This tutorial does not involve how to write middleware, because commonly used middleware is ready-made. You only need to reference modules written by others. That’s it. For example, the log middleware in the previous section has a ready-made redux-logger module. Here we only introduce how to use middleware.

import { applyMiddleware, createStore } from 'redux';
import createLogger from 'redux-logger';
const logger = createLogger();
 
const store = createStore(
  reducer,
  applyMiddleware(logger)
);

In the above code, redux-logger provides a generator createLogger, which can generate log middleware logger. Then, put it in the applyMiddleware method and pass it into the createStore method to complete the functional enhancement of store.dispatch().

There are two points to note here:

(1) The createStore method can accept the initial state of the entire application as a parameter. In that case, applyMiddleware will be the third parameter.

const store = createStore(
  reducer,
  initial_state,
  applyMiddleware(logger)
);

(2) The order of middleware should be particular.

const store = createStore(
  reducer,
  applyMiddleware(thunk, promise, logger)
);

In the above code, the three parameters of the applyMiddleware method are three middlewares. Some middleware has order requirements, so check the documentation before using it. For example, logger must be placed at the end, otherwise the output results will be incorrect.

3. applyMiddlewares()

Seeing this, you may ask, what exactly does the applyMiddlewares method do?

It is a native method of Redux. Its function is to form an array of all middleware and execute it in sequence. Below is its source code.

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}
  }
}

In the above code, all middleware are put into an array chain, then nested for execution, and finally store.dispatch is executed. As you can see, the getState and dispatch methods are available inside the middleware (middlewareAPI).

4. Basic ideas of asynchronous operations

After understanding middleware, you can handle asynchronous operations.

Synchronous operations only need to issue one Action. The difference between asynchronous operations is that they need to issue three Actions.

  • Action when the operation is initiated

  • Action when the operation is successful

  • Action when the operation fails Action

Taking retrieving data from the server as an example, the three Actions can be written in two different ways.

// 写法一:名称相同,参数不同
{ 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: { ... } }

In addition to different Action types, the State of asynchronous operations must also be modified to reflect different operation states. Below is an example of State.

let state = {
  // ... 
  isFetching: true,
  didInvalidate: true,
  lastUpdated: 'xxxxxxx'
};

In the above code, the property isFetching of State indicates whether data is being fetched. didInvalidate indicates whether the data is out of date, and lastUpdated indicates the last update time.

Now, the idea of ​​the entire asynchronous operation is very clear.

  • When the operation starts, send an Action, trigger the State to be updated to the "operating" state, and View re-renders

  • After the operation is completed, send again An Action is issued, triggering the State to be updated to the "Operation Ended" state, and the View is re-rendered again

##5. redux-thunk middleware

Asynchronous operations must send at least two Actions: the user triggers the first Action, which is the same as the synchronous operation, no problem; how can the system automatically send the second Action at the end of the operation?

The secret is in Action Creator.

class AsyncApp extends Component {
  componentDidMount() {
    const { dispatch, selectedPost } = this.props
    dispatch(fetchPosts(selectedPost))
  }
// ...

The above code is an example of an asynchronous component. After loading successfully (componentDidMount method), it sends (dispatch method) an Action to request data fetchPosts(selectedSubreddit) from the server. The fetchPosts here is Action Creator.

The following is the code of fetchPosts, the key point is in it.

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))。

上面代码中,有几个地方需要注意。

(1)fetchPosts返回了一个函数,而普通的 Action Creator 默认返回一个对象。

(2)返回的函数的参数是dispatch和getState这两个 Redux 方法,普通的 Action Creator 的参数是 Action 的内容。

(3)在返回的函数之中,先发出一个 Action(requestPosts(postTitle)),表示操作开始。

(4)异步操作结束之后,再发出一个 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.0
const 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。

The above is the detailed content of What is react middleware?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn