Hacker News new | ask | show | jobs
by savanaly 3179 days ago
The rationale sections reads:

>Higher order components are used to wrap other components. This component enables you to use connect straightforwardly within jsx, removing much of the cognitive burden of using connect and refactoring components to use connect.

I don't understand the rationale after reading that though. An example of the cognitive burden of rewriting a component to use connect would be appreciated. I write components using the connect HOC all day at work and have never felt any cognitive burden.

By the way, I always use decorator syntax to apply HOC's to my components, and perhaps that is why I don't think it's hard to parse at all? For example:

  @connect(mapStateToProps, mapDispatchToProps)
  class MyComponent extends React.Component {
    // etc.
  }

  export default MyComponent;
2 comments

FWIW, both the React and Redux teams generally advise against the use of decorators, and I personally heavily advise against using `connect` as a decorator, for several reasons:

- It's still a Stage 2 proposal. Now, the Class Properties syntax, which the React team (and I) highly recommend using, is also not yet final (it recently advanced to Stage 3). However, the Class Properties syntax seems to be much more stable, the behavior it's implementing is a lot simpler, and _if_ by chance it happens to change in the future, it should be relatively easy to code-mod (and the React team has said they would release a code-mod if that happens). Meanwhile, the decorators spec has changed some already, including at a recent TC39 meeting, and the Babel plugins have also had to change behavior and implementation over time.

- It obscures the real class definition. The standard advice for testing Redux-connected components is to export the "plain" class separately as a named export, and `export default connect()(MyComponent)`, then import and test the plain version. If you use `@connect()` as a decorator, the plain version isn't accessible, and testing becomes more difficult.

- Going along with that, I've seen many questions about why `defaultProps` and `propTypes` don't work right when `@connect()` is used, and it's because those get applied to the wrapper component, not the plain component, and thus things don't work the way you would want them to.

I see no advantages to using `connect` as a decorator. I encourage people to write their `mapState` functions separately anyway for clarity and testability (instead of inline as an argument to `connect`), so it's just a matter of moving the line with `connect` elsewhere in the file and changing the syntax slightly.

Thanks for the well thought out response. Your second point about class definitions and ease of testing is making me reconsider my using decorators for HOC's.

To your first point, I would say the HOC's as decorators would be very easy to code mod as well. Just go to ever file with decorators on my components and rewrite it to use _.fp.pipe() or apply them longhand or whatever. It probably couldn't comfortably be done in an automated fashion but it would be straightforward and not require a lot of thought.

And to the third point I have never had those not work right when using connect. I would be interested in an example even if it's just a stack overflow question where it came up.

As a real quick example, purely off the top of my head:

    @connect(state => ({a : state.a})
    class MyComponent extends React.Component {}

    MyComponent.propTypes = {a : PropTypes.string.required, b : PropTypes.number.required}
    MyComponent.defaultProps = {b : 42}
In this example, both the propTypes and defaultProps definitions are being applied to the wrapper component, not the actual "plain" MyComponent class. So, while the required `b` prop might get satisfied almost accidentally from the defaultProps value, the required `a` prop won't exist on the wrapper, as the wrapper itself is extracting that value from the Redux state internally and passing it to the plain MyComponent.

I've definitely seen this pop up as a recurring question that's confused people.

Ah. That has never come up for me because I assign `propTypes` and `defaultProps` within the class definition using class properties. I had assumed you would be assuming I would too because you made mention of class properties in your post. Who would go to the trouble of setting up decorators without also using class properties syntax? Quite a few people, apparently, but the practice of using one and not the other baffles me.
Yeah, I actually wasn't 100% sure whether class properties + a decorator would play together as "expected". Thinking about it, I would guess they must, in which case defining those values as class properties also would be reasonable.
I don't understand what this "cognitive burden" and "refactoring" is, either.

Connecting an existing component simply means: declaring a `mapStateToProps` and `mapDispatchToProps` and exporting a `connect(...)(Component)`. Where's the refactoring?