2021年3月3日星期三

Redux - Calling storeAPI.disapatch(action) inside middleware causes 'too much recursion'

I'm working through the Redux fundamentals tutorial. In the section on 'Writing Custom Middleware', we learn that middleware are written as a series of three nested functions like so:

// Outer function:  function exampleMiddleware(storeAPI) {    return function wrapDispatch(next) {      return function handleAction(action) {        // Do anything here: pass the action onwards with next(action),        // or restart the pipeline with storeAPI.dispatch(action)        // Can also use storeAPI.getState() here          return next(action)      }    }  }  

exampleMiddleware is explained as follows:

exampleMiddleware: The outer function is actually the "middleware" itself. It will be called by applyMiddleware, and receives a storeAPI object containing the store's {dispatch, getState} functions. These are the same dispatch and getState functions that are actually part of the store. If you call this dispatch function, it will send the action to the start of the middleware pipeline. This is only called once.

I don't understand the second last sentence. If storeAPI.dispatch is the original store's dispatch method, then how does calling it "send the action back to the start of the middleware pipeline"? Since the original store's dispatch passes the action object to the root reducer, wouldn't you expect storeAPI.dispatch(action) to break the pipeline?

So I tried calling store.dispatch(action) inside one of the middlewares provided in src/exampleAddons/middleware.js of the example app to see what happens and got "too much recursion". Here's the demo.

It looks as though calling storeAPI.dispatch() inside a middleware actually calls the composed dispatch function of all the middlewares combined rather than the original store's dispatch, which would explain the recursion.

In applyMiddleware's source:

function applyMiddleware(...middlewares) {    return createStore => (...args) => {      // ...1) createStore is called and the resulting store is saved as `store`      const store = createStore(...args)            // ...2) a `dispatch` variable is defined and assigned some function      let dispatch = () => {        throw new Error(          'Dispatching while constructing your middleware is not allowed. ' +            'Other middleware would not be applied to this dispatch.'        )      }            // ...3) a middlewareAPI object is defined containing the store's getState method and the `dispatch` function from 2).      const middlewareAPI = {        getState: store.getState,        dispatch: (...args) => dispatch(...args)      }            // ...4) the middlewares passed to applyMiddleware are called with the `middlewareAPI` object from 3) and the resulting functions are saved in array `chain`.      const chain = middlewares.map(middleware => middleware(middlewareAPI))            // ...5) the middlewares are composed and the resulting "composed" middleware function is called with `store.dispatch`.       // This returns a composed dispatch function that chains together the `handleAction` functions of all the middlewares passed to applyMiddleware.       // This composed dispatch gets assigned to the `dispatch` variable from 2).       // Since the `storeAPI.dispatch` is referencing this variable, calling `storeAPI.dispatch` now calls the composed middleware function, which causes the infinite loop.       dispatch = compose(...chain)(store.dispatch)        return {        ...store,        dispatch      }    }  }  

So the infinite loop seems to be the result of the re-assignment at step 5 in the annotations above, but I'm not sure if my reasoning is correct and would appreciate any guidance. I also wasn't able to find any examples that call storeAPI.dispatch(), so I'm not sure if I'm using it correctly.

https://stackoverflow.com/questions/66467615/redux-calling-storeapi-disapatchaction-inside-middleware-causes-too-much-re March 04, 2021 at 10:14AM

没有评论:

发表评论