Abstraction in React.js
Asked Answered
L

1

10

I want to use some abstraction in the creation of my React components. For example:

class AbstractButton extends React.Component {
  render() {
    return (
      <button
        onClick={this.props.onClick}
        className={this.definitions.className}>
        {this.props.text}
      </button>
    }
}
class PrimaryButton extends AbstractButton {
  constructor(options) {
    super(options);
    this.definitions = {
        className: 'btn btn-primary'
    };
  }
}
class SuccessButton extends AbstractButton {
  constructor(options) {
    super(options);
    this.definitions = {
        className: 'btn btn-success'
    };
  }
}

I don't want to pass these definitions via props because I know that these definitions--in this case the class--will never change.

Is it an anti-pattern in React? Or is it OK?

My question refers to this altjs issue: this kind of abstraction isn't compatible with @connectToStores.

Lamrouex answered 24/11, 2015 at 13:10 Comment(2)
React tends to focus on Composition, rather than inheritance, but your code doesn't look evil to me.Ansela
Yes, It is in general considered an anti-pattern. Here's an interesting article about it: medium.com/@dan_abramov/…Distiller
L
11

Generally speaking, there's no reason not to use composition here instead of deep inheritance:

class Button extends React.Component {
  render() {
    return (<button
             onClick={this.props.onClick}
             className={this.props.className}
           >
             {this.props.text}
           </button>);
  }
  static propTypes = {
      className: React.PropTypes.string.isRequired,
      onClick: React.PropTypes.func
  }
}

class PrimaryButton extends React.Component {
  render() {
    return <Button {...this.props} className="btn btn-primary" />;
  }
}

This is just as functional as what you propose, but is a lot simpler and easier to reason about. It makes it very clear what information your Button actually needs to do its work.

Once you make this leap, you can eliminate the classes altogether and use stateless components:

const Button = (props) => (<button
             onClick={props.onClick}
             className={props.className}
           >
             {props.text}
           </button>);
Button.propTypes = {
      className: React.PropTypes.string.isRequired,
      onClick: React.PropTypes.func
};

const PrimaryButton = (props) =>
    <Button {...props} className="btn btn-primary" />;

const SuccessButton = (props) =>
    <Button {...props} className="btn btn-success" />;

This will allow React to apply more optimizations since your components do not need any special lifecycle event support or state management. It is also even easier to reason about since now you are just working with pure functions.

As an aside, if you are trying to make some React components that wrap Bootstrap, then perhaps you should take a look at React-Bootstrap.

Lister answered 24/11, 2015 at 15:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.