【问题标题】:How does "next" work in react-redux middleware?react-redux 中间件中的“下一个”如何工作?
【发布时间】:2020-07-18 14:20:26
【问题描述】:

我是 React 和 Redux 的新手。我有一个简单的应用程序,您可以在其中将输入文本添加到无序列表中。我有防止显示特定单词的中间件。我不明白实现中间件的操作顺序。据我了解,当我触发调度时,会发生以下情况:

  1. 从表单提交事件触发调度
// title is an object with a string
function handleSubmit(event) {
    props.addArticle(title);
}
  1. 通过中间件函数:
// store.js
const store = createStore(
  rootReducer, // this is defined elsewhere and effectively handles actions
  applyMiddleware(forbiddenWordsMiddleware)
);
// middleware.js
import { ADD_ARTICLE } from "../constants/constants.js";
const forbiddenWords = ["spam", "money"];
export function forbiddenWordsMiddleware({ dispatch }) {
    return function (next) {
        return function (action) {
            if (action.type === ADD_ARTICLE) {
                const foundWord = forbiddenWords.filter((word) =>
                    action.payload.title.includes(word)
                );
                if (foundWord.length) {
                    return dispatch({ type: "FOUND_BAD_WORD" });
                }
            }
            return next(action);
        };
    };
}

上述函数实际上如何与 createStore 和 applyMiddleware 一起工作?让我特别困惑的是next(action) 行。下一步和行动从何而来?我无法想象从表单提交到检查禁用词的线性执行。

【问题讨论】:

    标签: javascript reactjs redux react-redux


    【解决方案1】:

    在此上下文中,next 仅表示此中间件对特定操作不感兴趣,并希望将其传递给其他中间件来处理它。

    当您将中间件提供给 applyMiddleware 函数时,它首先调用每个中间件并将名为 middlewareAPIhere 的内容传递给它。然后从最后一个中间件开始,调用它并将store.dispatch 给它,然后将它的结果发送到中间件,中间件在最后一个之前。并继续这样(将后一个中间件的结果传递给它之前的中间件)一直到第一个中间件。

    假设你有这三个中间件:[a, b, c],你将它们全部传递给 applyMiddleware 函数。这是发生了什么:

    1. 首先,使用middlewareAPI 对象调用每个中间件,该对象基本上是一个包含原始store.dispatchstore.getState 的对象。

      const middlewareAPI = { getState: store.getState, dispatch: store.dispatch }
      const aInChain = a(middlewareAPI);
      const bInChain = b(middlewareAPI);
      const cInChain = c(middlewareAPI);
      
    2. 其次,我们调用每个中间件,其结果是之前调用中间件的结果。期待最后一个中间件,它后面没有任何内容,并获取原始调度作为输入。

      const finalC = cInChain(store.dispatch);
      const finalB = bInChain(finalC);
      const finalA = aInChain(finalB);
      
    3. 然后我们将第一个中间件的最终版本设置为新增强存储的调度。因此,例如,当您调用 store.dispatch({ type: "Hello" }) 时,将调用 finalA 函数,如果它调用作为 next 提供给它的 finalB 函数,那么链中的下一个中间件将通过任何操作调用finalA 给它。整个链条都是这样的。

    所以在我们的问题示例中,中间件中有两个返回语句。第一个是return dispatch({...}),第二个是return next({...})

    在这两个中,我们的中间件都说它已经完成了这个动作的工作,并让调度链继续;但是在第一个return语句中,中间件直接调用了原来的dispatch,并将一个新类型的新动作传递给dispatch。所以最初传递给我们中间件的动作将被完全遗忘。该语句在 action 到达 store 之前中断了中间件的链,并使用新的 action 开始新的 dispatch。

    在第二次返回中,我们的中间件调用next 函数,正如我之前所描述的,它是该行中的下一个中间件,我们的中间件将原始的action 发送给它而不做任何更改。所以结果就像这个中间件根本不存在一样。

    在这种情况下,我们的中间件只是说“我不关心这个动作是什么;我想将它发送到下一个中​​间件来决定它是否应该到达store.dispatch”。

    【讨论】:

    • 你的最后一段有很多“它”和像“中间件”这样的一般性陈述。你能编辑你的帖子并添加更多的特异性吗?例如:“在他们两个中,中间件都说”。哪个中间件?
    • 原始调度操作何时/如何到达商店?我认为线性表示的东西会有所帮助,类似于上面。
    【解决方案2】:

    中间件围绕实际的 Redux store.dispatch 函数形成管道。当您调用store.dispatch(action) 时,您实际上是在调用链中的第一个中间件。在中间件内部,next passes a value to the next middleware, while storeAPI.dispatch restarts the pipelineaction 是传递给dispatch 的值(或从之前的中间件传递给next)。

    这种可视化可能会有所帮助:

    诚然,实际的实现是a bit of magical code,但您不必担心。

    更多详情,请参阅the "Extensibility and middleware" section of my "Redux Fundamentals Workshop" slides,以及these articles on how Redux middleware work

    【讨论】:

    • 两个问题:“next”只是指向下一个中间件函数的指针吗?回到第一个中间件后如何继续执行?
    • 严格来说,next 管道中的下一个中间件函数(或者,如果这是最后一个中间件,next 是真正的 store.dispatch 方法本身)。如果您调用dispatch(),则整个管道序列将根据图表重新启动。
    • 图中中间件3是什么?
    • 任何你想要的中间件。重点是说明数据流,不用担心具体中间件是如何实现的细节。
    • 感谢您的回答@markerikson。我知道storeAPI.dispatch 会重新启动管道,但我不确定如何在不导致无限循环的情况下使用它。方便时可以看看my question吗?
    猜你喜欢
    • 2017-02-08
    • 1970-01-01
    • 1970-01-01
    • 2018-06-30
    • 2017-08-23
    • 2018-05-15
    • 1970-01-01
    • 1970-01-01
    • 2017-01-24
    相关资源
    最近更新 更多