【问题标题】:Nested reducers嵌套减速器
【发布时间】:2016-03-22 17:49:49
【问题描述】:

为了学习 Redux,我正在创建一个简单的 Web 应用程序。

如果存在验证用户名和密码的“身份验证”操作。然后我想要调用“登录”或“失败”操作。我不知道怎么做。 我可以调用身份验证步骤,但嵌套/后续调用未执行。

如何从另一个函数中调用一个函数? (这就是我的问题令人沮丧的原因^^)

我的代码如下所示:

function authenticateClient(state, client){
    if (client.password === 'perfectsec')
        return dispatch(Actions.connectClient(client));
    else
        return dispatch(Actions.refuseClient(client));
}

在 reducer 中调用 dispatch 是不正确的。最简单的方法是直接调用函数connectClient(state, client)refuseClient(state, client)。但这一步并没有经过 redux 流程。


上下文代码:

/* actions.js */

export function authenticateClient(client) {
  return { type: Types.authenticateClient, client };
}

export function connectClient(client, isNew) {
  return { type: Types.connectClient, client, isNew };
}

export function refuseClient(client) {
  return { type: Types.refuseClient, client };
}


/* main.js */

/* reducer */
const reducer = (state = loadState(), action) => {
  console.log(chalk.red(`*${action.type}*`));
  switch (action.type) {
    case Actions.Types.authenticateClient:
      return authenticateClient(state, action);
    case Actions.Types.connectClient:
      return connectClient(state, action);
    case Actions.Types.refuseClient:
      return refuseClient(state, action);
    default:
      return state;
  }
};


/* store */
store = createStore(reducer);


/* app */
store.dispatch(Actions.authenticateClient({username, password}));

【问题讨论】:

  • 我不是 100% 能够遵循你的代码,所以只是澄清一下 - 你不是试图从减速器内部调度动作,是吗?因为那是你 100%从不应该调度操作的地方。
  • 谢谢。是的,这正是我想要做的。我注意到这是不正确的。但我不明白如何实现我的目标。我添加了一段,试图澄清。
  • 如果你需要有条件地调度动作,这个逻辑很可能在另一个动作创建者中(例如Actions.connectClientIfValid(client))——如果输入有效,则返回Actions.connectClient(client)的结果,如果不是,返回Actions.refuseClient(client) 的结果。然后,您可以分派返回的任何内容。你的 reducer 应该做的就是 state + action = newState - 它不应该对你的应用程序的其余部分产生副作用。
  • 也就是说,如果由于调用了一个动作创建者而需要调度多个动作,它会变得更加复杂 - 我真的建议阅读 Redux 网站上的教程并遵循作者制作的截屏视频,因为它涵盖了所有这些东西,而且这个家伙解释得比我可能做得更好:)
  • 那么小逻辑 IF valid THEN connect ELSE refuse 应该在哪里?我在 3 个月前阅读并观看了所有这些内容,现在尝试一下,也许我应该重复一遍。感谢您的帮助!

标签: javascript redux reducers


【解决方案1】:

Reducer 永远不应该派发动作,或者做任何其他有副作用的事情。所有 reducer 都应该是状态和操作的纯函数(实际上是 state + action = newState)——整个 Redux 库都是围绕这个假设构建的,我相信它是专门设计用来在你尝试在 reducer 中调用 dispatch 时抛出错误的.

有条件地分派动作的最佳方式是创建一个新的“动作创建者” - 您的应用程序中已经有其中一些:

export function authenticateClient(client) {
  return { type: Types.authenticateClient, client };
}

export function connectClient(client, isNew) {
  return { type: Types.connectClient, client, isNew };
}

export function refuseClient(client) {
  return { type: Types.refuseClient, client };
}

请注意,标准动作创建者不会自己调度动作。它们只是返回一个动作的函数,你可以在某个时候调度自己(如果你想创建一个动作并在实际发送它之前保持一段时间,这很方便)。一旦你开始编写异步动作创建者,情况就会变得有些混乱,但这并不在你的问题范围内,所以我现在将跳过它(不过,我建议阅读文档的异步部分!你真的可以做一些一旦你了解它,就会发现很酷的东西)。

您在这里试图表达的是一个函数,它说,'如果条件为真,给我一个connectClient 动作,如果不是,给我一个refuseClient 动作'。我们已经知道为您提供动作的函数是动作创建者,这将我们引向这个答案的要点:动作创建者可以有逻辑。虽然它们中的 90% 只会接收数据并立即返回一个对象,但这并不是它们可以采用的唯一形式。以下是如何表示此功能的示例:

export function connectClientIfValid(client) {
  if (client.valid === true) {
    return connectClient(client);
  }
  else {
    return refuseClient(client);
  }
}

同样,这个函数实际上并没有发送任何内容 - 它只是返回一个动作,您可以在闲暇时调度它,如下所示:

dispatch(connectClientIfValid(client));

您的逻辑运行,并分派适当的操作,而不必损害减速器的纯/同步性质。您的 reducer 所要做的就是在动作进入时读取它们,并适当地更新状态 - 它不必关心导致动作发生的逻辑。

【讨论】:

  • 再次感谢您的耐心等待。时间会证明我是否真的掌握了它。正如您所指出的,reducer 和 action creator 之间的区别很重要。我试图做的只是一个“子减速器”。我的 authenticateClient 是一个减速器。对于这个用例,我可以使用combineReducers
猜你喜欢
  • 1970-01-01
  • 2019-04-06
  • 1970-01-01
  • 2016-04-16
  • 1970-01-01
  • 2016-06-04
  • 2020-07-07
  • 2021-02-10
  • 1970-01-01
相关资源
最近更新 更多