简体   繁体   English

Redux:在reducer函数中调用store.getState(),这是一种反模式吗?

[英]Redux: Calling store.getState() in a reducer function, is that an anti pattern?

I'm wondering, sometimes I have a reducer that needs information from another reducer. 我想知道,有时我有一台需要另一台减速机信息的减速机。 For example I have this reducer: 例如,我有这个减速器:

import * as ActionTypes from '../actions/action_type_constants';
import KeyCode from 'keycode.js/index';
import {store} from "../index";
import {mod} from "../pure_functions";

export function selectedCompletion(state = 0, action) {
  if (action.type === ActionTypes.arrowKeyPressed) {
    const completionsLength = store.getState().completions.data.length;
    if (action.keyCode === KeyCode.UP) {
      return mod(state - 1, completionsLength);
    } else if (action.keyCode === KeyCode.DOWN) {
      return mod(state + 1, completionsLength);
    }
  }
  return state;
}

I do call store.getState at the second line of the function, because otherwise I can not determine the index correctly. 我在函数的第二行调用store.getState,否则我无法正确确定索引。

I could probably refactor this and the other reducer, so that it becomes one big reducer, but for readability I would prefer this option. 我可以重构这个和另一个减速器,以便它成为一个大的减速器,但为了可读性,我更喜欢这个选项。

I'm not sure if I would get somehow into problems if I use this pattern of calling store.getState() in a reducer. 如果我在reducer中使用这种调用store.getState()的模式,我不确定是否会以某种方式解决问题。

Yes, this is absolutely an anti-pattern. 是的,这绝对是一种反模式。 Reducer functions should be "pure", and only based on their direct inputs (the current state and the action). 减速器功能应该是“纯粹的”,并且仅基于它们的直接输入(当前状态和动作)。

The Redux FAQ discusses this kind of issue, in the FAQ on sharing state between reducers . Redux FAQ在关于reducers之间共享状态的 FAQ中讨论了这类问题。 Basically, you should either write some custom reducer logic that passes down the additional information needed, or put more information into your action. 基本上,您应该编写一些自定义的reducer逻辑,传递所需的其他信息,或将更多信息添加到您的操作中。

I also wrote a section for the Redux docs called Structuring Reducers , which discusses a number of important concepts related to reducer logic. 我还为Redux文档编写了一个名为Structuring Reducers的部分 ,该部分讨论了与reducer逻辑相关的一些重要概念。 I'd encourage you to read through that. 我鼓励你仔细阅读。

The pattern you want is a case of composition because you are preparing new state based in other existing states from other domain (in the sense of reducer domains). 您想要的模式是组合的情况,因为您正在准备基于其他域(在reducer域意义上)的其他现有状态的新状态。 In the Redux documentation an example is provided under the topic entitled Computing Derived States . 在Redux文档中,在题为计算派生状态的主题下提供了一个示例。

Notice that their sample, however, combined the states in the container - not in the reducer; 请注意,他们的样本将容器中的状态组合在一起 - 而不是在减速器中; yet feeding the component that needs it. 然后喂养需要它的组件。

For these coming to this page wondering how to upgrade their large applications to redux 4.0 without revamping their complete statemanagement because getState etc were banned in reducers: 对于这些来到这个页面想知道如何将他们的大型应用程序升级到redux 4.0而不改进他们的完整状态管理,因为getState等在reducers中被禁止:

While I, like the authors, dissaprove of that antipattern, when you realize that they banned the usage of these functions without this without any technical reasons and without opt-out, leaving people without updates that have the misfortune of having codebases which broadly use this antipattern... Well, I made a merge request to add an opt-out with heavy guards, but it was just immediately closed. 虽然我和作者一样,反对这种反模式,当你意识到他们在没有任何技术原因且没有选择退出的情况下禁止使用这些功能时,让没有更新的人不幸拥有广泛使用这个的代码库反模式...好吧,我做了一个合并请求,用重型警卫添加一个选择退出,但它只是立即关闭。

So I created a fork of redux, that allows to disable the bans upon creating the store: 所以我创建了一个redux fork,允许在创建存储时禁用禁令:

https://www.npmjs.com/package/free-redux https://www.npmjs.com/package/free-redux

For anyone else, use one of the examples here or the way I provided in another question : 对于其他任何人,请使用此处的示例之一或我在另一个问题中提供的方式:

An alternative way, if you use react-redux and need that action only in one place OR are fine with creating an HOC (Higher oder component, dont really need to understand that the important stuff is that this might bloat your html) everywhere you need that access is to use mergeprops with the additional parameters being passed to the action: 另一种方法,如果你使用react-redux并且只在一个地方需要这个动作,或者可以创建一个HOC(更高级的组件,真的不需要理解重要的东西,这可能会使你的html膨胀),无论你需要什么该访问是使用mergeprops与传递给操作的其他参数:

const mapState = ({accountDetails: {stateOfResidenceId}}) => stateOfResidenceId;

const mapDispatch = (dispatch) => ({
  pureUpdateProduct: (stateOfResidenceId) => dispatch({ type: types.UPDATE_PRODUCT, payload: stateOfResidenceId })
});

const mergeProps = (stateOfResidenceId, { pureUpdateProduct}) => ({hydratedUpdateProduct: () => pureUpdateProduct(stateOfResidenceId )});

const addHydratedUpdateProduct = connect(mapState, mapDispatch, mergeProps)

export default addHydratedUpdateProduct(ReactComponent);

export const OtherHydratedComponent = addHydratedUpdateProduct(OtherComponent)

When you use mergeProps what you return there will be added to the props, mapState and mapDispatch will only serve to provide the arguments for mergeProps. 当你使用mergeProps返回的东西将被添加到props中时,mapState和mapDispatch将仅用于为mergeProps提供参数。 So, in other words, this function will add this to your component props (typescript syntax): 所以,换句话说,这个函数会将它添加到你的组件props(typescript语法):

{hydratedUpdateProduct: () => void}

(take note that the function actually returns the action itself and not void, but you'll ignore that in most cases). (请注意,该函数实际上返回了操作本身而不是void,但在大多数情况下你会忽略它)。

But what you can do is: 但你能做的是:

const mapState = ({ accountDetails }) => accountDetails;

const mapDispatch = (dispatch) => ({
  pureUpdateProduct: (stateOfResidenceId) => dispatch({ type: types.UPDATE_PRODUCT, payload: stateOfResidenceId })
  otherAction: (param) => dispatch(otherAction(param))
});

const mergeProps = ({ stateOfResidenceId, ...passAlong }, { pureUpdateProduct, ... otherActions}) => ({
  ...passAlong,
  ...otherActions,
  hydratedUpdateProduct: () => pureUpdateProduct(stateOfResidenceId ),
});

const reduxPropsIncludingHydratedAction= connect(mapState, mapDispatch, mergeProps)

export default reduxPropsIncludingHydratedAction(ReactComponent);

this will provide the following stuff to the props: 这将为道具提供以下内容:

{
  hydratedUpdateProduct: () => void,
  otherAction: (param) => void,
  accountType: string,
  accountNumber: string,
  product: string,
}

On the whole though the complete dissaproval the redux-maintainers show to expanding the functionality of their package to include such wishes in a good way, which would create a pattern for these functionalities WITHOUT supporting fragmentation of the ecosystem, is impressive. 总的来说,虽然完全无法解决,但redux维护者表示扩展其软件包的功能,以便以良好的方式包含这些愿望,这将为这些功能创建一个模式,而不支持生态系统的碎片化,令人印象深刻。

Packages like Vuex that are not so stubborn dont have nearly so many issues with people abusing antipatterns because they get lost, while supporting a way cleaner syntax with less boilerplate than you'll ever archive with redux and the best supporting packages. 像Vuex这样不那么顽固的软件包并没有人们滥用反模式的问题,因为他们迷路了,而支持一种更简洁的语法,用更少的样板文件比用everx和最好的支持软件包存档更少。 And despite the package being way more versatile the documantation is easier to understand because they dont get lost in the details like reduxs documentation tends to do. 尽管这个包更加通用,但是文档更容易理解,因为它们不会像reduxs文档那样迷失在细节中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM