Donald Taylor Donald Taylor - 1 month ago 16
React JSX Question

Props in inheritance and composition

I have a React class named

Panel
that I would like to serve as a reusable component for various kinds of specific panels in a UI. Each panel has in common a title bar and a "Submit" button, but the body of each kind of panel is unique.

I could use either inheritance (sub-classing) or composition to achieve this, but which would be best in this case?

I have tried sub-classing, having a render method in the parent
Panel
and then having child panels override a
renderBody
method, which
render
uses. That seems to break down because each specific panel needs its own props (such as a "title"), and React complains when modified props are passed to
super
in a component constructor (the error message is, "When calling super() . . . make sure to pass up the same props that your component's constructor was passed."). Since the "title" is specific to a kind of panel, I don't want the end consumer having to specify a "title" prop itself.

class Panel extends React.Component {

render() {
return (
<div>
<div>{this.props.title}</div>
<div>{this.renderBody()}</div>
<div><button>Submit</button></div>
</div>
)
}

}

class SomeSubPanel extends Panel {

constructor(props) {
// React throws the error message at the following line
let newProps = Object.assign({}, props, {title: "Some Sub Panel"})
super(newProps)
}

renderBody() {
return (<div>Panel Body Goes Here</div>)
}

}


Using composition wouldn't seem to be as tidy as sub-classing because each panel only needs to have a specific body, yet the body (which is HTML) can't be passed between components.

What would be the "React way" of having child components that can pass traits to a reusable parent component?

Many thanks!

Answer

Definitely use composition. Generally, I don't think you should ever extend your own React components. Here's how you could achieve it:

class ReusablePanel extends React.Component {
  render () {
    return (
      <div>
        <div>{this.props.title}</div>
        <button onClick={this.props.onSubmit}>Submit</button>
        {this.props.children}
      </div>
    )
  }
}

class FootballPanel extends React.Component {
  handleSubmitButtonClick = () => {
    // do something
  }

  render () {
    return (
      <ReusablePanel title='Football' onSubmit={this.handleSubmitButtonClick}>
        <div>{/* Football markup */}</div>
      </ReusablePanel>
    )
  }
}

class ArsenalPanel extends React.Component {
  handleSubmitButtonClick = () => {
    // do something
  }

  render () {
    return (
      <ReusablePanel title='Arsenal' onSubmit={this.handleSubmitButtonClick}>
        <div>{/* Arsenal markup */}</div>
      </ReusablePanel>
    )
  }
}
Comments