Codementor Events

Enhancing React Components: Inheritance & Composition

Published Dec 26, 2016Last updated Jan 18, 2017
Enhancing React Components: Inheritance & Composition

Building React components is fun, but it can get tedious too!

One look at material-ui would tell us how beautiful components can be built with React. Building components is very easy in React, because well, components is the USP of React, and Facebook has done everything in its power to make building components easy. However, well, there are still challenges.

Most web pages need various styles of a single component. For example, a button in one component is red in color, and in another, it's blue with a black border. A navigation bar looks full width on a desktop, but its full-height collapsible in mobiles. Components which are so similar, yet different. Do we end up writing two buttons and two navbars each time?

No! There should be another way. What are some ways to write lesser code and more flexible components?

There are a couple of ways of enhancing React components.

  1. Inheritance
  2. Composition

Now, understanding the pros and cons of both techniques is beyond the scope of this tutorial. However, this video serves as a good background to what these 2 methods are and how they stack up against each other.

Inheritance in React (or Plain JavaScript Classes)

Inheritance is a concept in JavaScript which allows one class to inherit another class's properties to duplicate behavior and add more features. For example, a Label component can be extended into a SuccessLabel with two additional properties background: green and color: white.

Similarly, an ErrorLabel can be extended from Label with its own additional properties like color: red and so on and so forth.

But the bottom line of inheritance is that a SuccessLabel instance is a Label and inherits the behavior of a Label instance.

Also, a SuccessLabel can be further extended to create an even more enhanced class and this process can go further levels deep. No matter how many levels deep we go, the resulting instance will still have the entire behavior of Label within it and instanceof operator will return true when used with a SuccessLabel instance against the Label class.

Alright, so how would we use inheritance with React components?

Before we dive into the JS part, for the course of the tutorial, our CSS is as shown below.

CSS

html,body{
  padding: 20px;
}
h3{
  margin : 40px 0 20px;
}
hr{
  border-color : transparent;
  margin : 10px;
}
.plain-label{
  font-size: 16px;
  background : #f5f5f5;
  border: 1px solid #333;
  padding: 4px 8px;
}
.success-label{
  background : green;
  color : white;
}

.error-label{
  background : red;
  color : white;
}

To inherit one React component class from another, we can write the code like so:

Javascript

class Label extends React.Component{
  constructor(props){
    super(props);
    this.className='plain-label';
  }
   render(){
     return <span className={this.className}>
        {this.props.children} 
      </span>
   }
}

class SuccessLabel extends Label{
  constructor(props){
    super(props);
    this.className = this.className + ' success-label';
  }
}

class SuccessLabelWithIcon extends Label1{
  constructor(props){
    super(props);
    this.className = this.className + ' success-label';
  }
  render(){
    return <div>
          {super.render()}<span>&#9650;</span>
     </div>
  }
}

class ErrorLabel extends Label{
  constructor(props){
    super(props);
    this.className = this.className + ' error-label';
  }
}


class Main extends React.Component{
    render(){
      ....
      <Label> Plain Label </Label>
      <hr/>
      <SuccessLabel> Success Label </SuccessLabel>
      <hr/>
      <ErrorLabel> Error Label </ErrorLabel>  
       <hr/>
       <SuccessLabelWithIcon> Success Label with Icon </SuccessLabelWithIcon>
     ...
    }

}

Result

React components

The code above relies heavily on the Label component and its render method. The render method relies on this.className (set within the constructor for each extended class) property which is different for different extended classes and hence the styling varies with the CSS attached to the className rendered.

One of the disadvantages of inheritance is clearly visible in the last case.SuccessLabelWithIcon class is a SuccessLabel but aims to show an icon to the right of the label text. However, with the current code, it is unable to do so. Unless we do something like this.

//SucessLabelWithIcon Component

  ....
   render(){
        return
        .....
        {super.render(<span>&#9650;</span>)}
        .....
   }


//SuccessLabel Component
  ....
   render(args){
        return
        .....
          //do something with args
        .....
   }

Now, this can't be the right way to do it. Because SuccessLabel has to accommodate the use cases of another class.

More use cases => more arguments and this becomes unmaintainable very soon.

Inheritance is in most scenarios — just not good a viable solution. Because extending components with inheritance more or less leads to a scenario at some point, where the behaviors cannot be clubbed seamlessly. However, with composition, its possible.

Composition in React

Composition is a technique which allows us to combine one or more components into a newer, enhanced component capable of greater behavior. Instead of extending another class or object, composition only aims at adding new behavior.

A SuccessLabel is not a Label but another component which happens to use a Label within it. The properties of Label are not inherited, only the behavior. Which means adding an Icon to a SuccessLabel should be fairly easy as well.

Let's try composition now.

JavaScript

class Label extends React.Component{

   render(){
     return <span className={this.props.className + ' plain-label'}>
        {this.props.children} 
      </span>
   }
}

class SuccessLabel extends React.Component{
  
  render(){
    return <Label className='success-label'>{this.props.children}</Label>;  
  }
}

class ErrorLabel extends React.Component{
  
  render(){
    return <Label className='error-label'>{this.props.children}</Label>;  
  }
}


class SuccessLabelWithIcon extends React.Component{
 render(){
     return <div>
      <Label className='success-label'>{this.props.children}<span>&#9650;</span></Label>
     </div>
  } 
}


class Main extends React.Component{
    render(){
      ....
      <Label> Plain Label </Label>
      <hr/>
      <SuccessLabel> Success Label </SuccessLabel>
      <hr/>
      <ErrorLabel> Error Label </ErrorLabel>  
       <hr/>
       <SuccessLabelWithIcon>SuccessLabel With Icon </SuccessLabelWithIcon>
     ...
    }

}

Result

React components

That was simple and easy, wasn't it? There are many ways of composing your React components and enhancing their behaviors. Since composition only aims at adding new behavior in a can-do fashion instead of is-a, it is just a matter of deciding which behaviors your component needs and just clubbing them together.

Similarly, here is a link to Codepen about what we have discussed in this tutorial. You can also read up on other important topics about React, like testing, in Jest and Enzyme or Karma and Webpack.

Happy coding!!

Discover and read more posts from Bhargav
get started
post comments7Replies
Santhan Frankenstein
6 years ago

good explanation

Oscar Salvador
6 years ago

Great Explanation. But what if SuccessLabel, ErrorLabel were more complicated components that need to handle some specific logic related to a Label. Wouldn’t inheritance be better in this case ?

Both SuccessLabel and ErrorLabel could use the share code handled by the parent class.

Santhan Frankenstein
6 years ago

then we have more props for label to handle those complexity

Oscar Salvador
6 years ago

I don’t think this the correct answer. Would have been ok if you mentioned HOC as an alternative but I still haven’t heard a valid argument as to why inheritance is no recommended in React. But thanks for this post. It was useful

nave hazan
6 years ago

The best explanation I’ve found so far Inheritance vs Composition in React…thanks!!!

Show more replies