简体   繁体   中英

How do you customize dispatches in React Redux containers?

The way React Redux is built requires you to dispatch changes from within the mapDispatchToProps() function. If I want to use the container (the higher order component that connects a React component to Redux) to access state, while, at the same time being able to dispatch different actions *(depending on the feature), I either:

  1. need to add all the ways of dispatching for all features to the mapDispatchToProps() function of the container that is accessing state
  2. need to create an additional container for each feature to wrap the container that is accessing state
  3. need to expose dispatch() to the feature through a callback prop

Is there a recommended implementation to customize which actions are dispatched that isn't in the list above? Specifically, why would option number three be a bad choice?

Example Use-case

I have a React presentational component (VerticalBarChart) and a React-Redux container component (VerticalBarChartContainer). I want to access data from the same location in state but need to handle when users click on the chart's bars differently (depending on the feature). In Feature A, I want my feature to act as if I have drilled into the data for that bar, in Feature BI just want to display more details about that bar's data in a modal.

Example 1 (all variations of dispatch inside of mapDispatchToProps() )

<!-- Feature A -->
<VerticalBarChartContainer bar-click-action="drill" />

<!-- Feature B -->
<VerticalBarChartContainer bar-click-action="showDetailsModal" />

Example 2 (wrapping the container in each feature)

<!-- Feature A (contains a VerticalBarChartContainer) -->
<FeatureAVerticalBarChartContainer />

<!-- Feature B (contains a VerticalBarChartContainer) -->
<FeatureBVerticalBarChartContainer />

Example 3 (exposing dispatch)

<!-- Feature A -->
<VerticalBarChartContainer 
   onBarClick={ (dispatch) => dispatch(action1()) } 
   />

<!-- Feature B -->
<VerticalBarChartContainer 
   onBarClick={ (dispatch) => dispatch(action2()) }
   />

A good rule of thumb is that a container should rarely contain JSX or define a component itself, rather it should just wrap inner components with HoCs. On that basis I think a good pattern is to fork the behaviour by creating multiple different containers all wrapping the same inner presentational component, which I think is your example 2 above.

If you want to dispatch an action conditional on state and/or props, you can do so through the mergeProps method, the third argument to connect . The mergeProps method receives state props (object returned from mapStateToProps ), dispatch props (object returned from mapDispatchToProps ), and the component props.

Here is an example usage (please not mapStateToProps and mapDispatchToProps methods are omitted since I assume you're familiar):

function mergeProps(stateProps = {}, dispatchProps = {}, ownProps = {}) {
  const { conditionA } = ownProps;
  const { conditionB } = stateProps;
  const { firstFn, secondFn } = dispatchProps;
  const onClick = (conditionA && conditionB) ? firstFn : secondFn;

  return {
    ...ownProps,
    ...dispatchProps,
    ...stateProps,
    onClick,
  };
}

export const ConnectedComponent = connect(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps,
)(Component);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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