简体   繁体   中英

Reducing React-Redux state & dispatch boilerplate

I have been generally building my components using the following pattern:

import selectors from '../store/selectors'
import someAction1 from '../actions/someAction1'
import someAction2 from '../actions/someAction2'
import someAction3 from '../actions/someAction3'

class MyComponent extends React.Component {
  render() => {
    return <Fragment>
      <div>{this.props.someReduxProperty1}</div>
      <div>{this.props.someReduxProperty2}</div>
      <div>{this.props.someReduxProperty3}</div>
      <a onClick={this.props.someAction1}>Action1</a>
      <a onClick={this.props.someAction2}>Action2</a>
      <a onClick={this.props.someAction3}>Action3</a> 
    </Fragment>
  }
}

export default connect(
  (state, ownProps) => {
    return {
      someReduxProperty1: selectors.getSomeReduxProperty1(state),
      someReduxProperty2: selectors.getSomeReduxProperty2(state),
      someReduxProperty3: selectors.getSomeReduxProperty3(state)
    }
  },
  (dispatch) => {
    return {
      someAction1: () => {
        dispatch(someAction1())
      },
      someAction2: () => {
        dispatch(someAction2())
      },
      someAction3: () => {
        dispatch(someAction3())
      }
    }
  }
)(MyComponent)

This does get the job done, but it feels like I am typing the same things repeatedly, even in this trivially short example. My container components feel quite verbose by the time they are functional. Additionally, I find that I am frequently modifying the list of properties & actions used in the component, which makes it a bit more frustrating to be constantly re-typing it all. Most of the examples I read about react+redux seem to mirror this code setup though.

Is this really necessary? I am contemplating ignoring what seems to be React+Redux best practices and just passing through the state and dispatch as props to my components:

import selectors from '../store/selectors'
import someAction1 from '../actions/someAction1'
import someAction2 from '../actions/someAction2'
import someAction3 from '../actions/someAction3'

class MyComponent extends React.Component {
  render() => {
    const { state, dispatch } = this.props
    return <Fragment>
      <div>{selectors.getSomeReduxProperty1(state)}</div>
      <div>{selectors.getSomeReduxProperty2(state)}</div>
      <div>{selectors.getSomeReduxProperty3(state)}</div>
      <a onClick={() => { dispatch(someAction1()) }}>Action1</a>
      <a onClick={() => { dispatch(someAction2()) }}>Action2</a>
      <a onClick={() => { dispatch(someAction3()) }}>Action3</a>
    </Fragment>
  }
}

export default connect(
  (state, ownProps) => {
    return { state: state }
  }
  /* dispatch gets added by default */
)(MyComponent)

The code only has minimal repetition of the variable names. An added bonus, since the connect call would be the same for every component, I could just wire up a helper and shorten up my components even more:

export default connectHelper = (componentClass) => {
  return connect((state) => {
    return { state: state }
  })(componentClass)
}
class MyComponent extends React.Component {
  ...
}

export default connectHelper(MyComponent)

I do lose some ability to see at a glance what properties are used from my Redux store just by skimming the connect call. I am okay with this.

So my question(s) are:

  1. Are there any other considerations that I would be sacrificing with essentially bypassing mapStatetoProps? One concern I have is that since there is just a single state property that React may overzealously re-render every component every time any property in the Redux store changes. Is this why it's necessary to map out the properties manually in mapStateToProps?

  2. What am I losing by skipping the mapDispatchToProps and utilizing the default injected this.props.dispatch ? I have to do "extra" work to map the states directly in the mapDispatchToProps; what do I get for this effort?

  3. Are there other code organization/patterns that assists with helping reduce Component property clutter in a React+Redux setup?

Thanks! Just getting my head wrapped around these tools. It's been a fun exercise, but some things have felt a bit quirky!

Yes, there's plenty of ways to shorten this.

First, you should be using the "object shorthand" form of mapDispatch :

const mapDispatch = {
    someAction1,
    someAction2,
    someAction3,
};

Second, while we don't have an "object shorthand" equivalent for mapState , you can use Reselect's createStructuredSelector API to do the same kind of thing.

And yes, if you pass along the entire state value, your component would now be re-rendering for every update to the Redux store. React-Redux does a lot of work internally to make sure your own component only re-renders when needed , and that's based on the values returned by your mapState function.

Third, consider using destructuring at the start of your components to pull out the specific props they need, so you're not repeating this.props. on every line. This can either be object destructuring in a class component's render() method, or function parameter destructuring for a function component.

Finally, while it's not React-specific, please check out our new redux-starter-kit package . It includes utilities to simplify several common Redux use cases, including store setup, defining reducers, immutable update logic, checking for accidental mutations, and even creating entire "slices" of state automatically without writing any action types or action creators by hand.

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