JavaScript Inheritance And Polymorphism In React JS Application

The React JS documentation emphasizes on using composition over inheritance for components, as there is hardly any scenario that requires component inheritance. However, that does not stop us from using inheritance and polymorphism within our JavaScript code. To explain, we discuss one such example below by using es6 classes:

Example Of Inheritance And Polymorphism

Say, we need to create a chess application in React. The most basic thing to start off with will be a Board and pieces Pawn, Rook, Knight, Bishop, Queen and King. At the minimum, all these pieces will have properties such as color, icon and player (player 1 or 2), and methods such as move.

Since we notice right away that there are properties and methods that all of the pieces share, such as color, player and move, it is only logical to create a parent class Piece and inherit all the pieces from it. This way, we can override move method in all the inherited pieces and implement polymorphism (call move on any of the piece instances without knowing what exact piece is it, and it will move correctly).

class Piece {
  constructor({ color, icon, player }) {
    this.color = color;
    this.icon = icon;
    this.player = player
  }

  move() {}
}

// The pieces below inherit the above properties and method
class Pawn extends Piece {
  constructor(pieceData) {
    super(pieceData)
  }

  move() {
    // move one step ahead, kill one step diagonally, first turn can be two steps
  }
}

class Rook extends Piece {
  constructor(pieceData) {
    super(pieceData)
  }

  move() {
    // move squarely
  }
}

class Knight extends Piece {
  constructor(pieceData) {
    super(pieceData)
  }

  move() {
    // move L shape
  }
}

class Bishop extends Piece {
  constructor(pieceData) {
    super(pieceData)
  }

  move() {
    // move diagonally
  }
}

class Queen extends Piece {
  constructor(pieceData) {
    super(pieceData)
  }

  move() {
    // move squarely or diagonally
  }
}

class King extends Piece {
  constructor(pieceData) {
    super(pieceData)
  }

  move() {
    // move squarely or diagonally one step
  }
}

With the above pieces, we can fill our 8X8 2D-array of board:

const board = [
  [
    new Rook({ color: "black", icon: "rook", player: 2 }),
    new Rook({ color: "black", icon: "knight", player: 2 }),
    new Rook({ color: "black", icon: "bishop", player: 2 }),
    new Rook({ color: "black", icon: "queen", player: 2 }),
    new Rook({ color: "black", icon: "king", player: 2 }),
    new Rook({ color: "black", icon: "bishop", player: 2 }),
    new Rook({ color: "black", icon: "knight", player: 2 }),
    new Rook({ color: "black", icon: "rook", player: 2 }),
  ],
  [
    new Rook({ color: "black", icon: "pawn", player: 2 }),
    new Rook({ color: "black", icon: "pawn", player: 2 }),
    new Rook({ color: "black", icon: "pawn", player: 2 }),
    new Rook({ color: "black", icon: "pawn", player: 2 }),
    new Rook({ color: "black", icon: "pawn", player: 2 }),
    new Rook({ color: "black", icon: "pawn", player: 2 }),
    new Rook({ color: "black", icon: "pawn", player: 2 }),
    new Rook({ color: "black", icon: "pawn", player: 2 }),
  ],
  [null, null, null, null, null, null, null, null],
  [null, null, null, null, null, null, null, null],
  [null, null, null, null, null, null, null, null],
  [null, null, null, null, null, null, null, null],
  [
    new Rook({ color: "white", icon: "pawn", player: 1 }),
    new Rook({ color: "white", icon: "pawn", player: 1 }),
    new Rook({ color: "white", icon: "pawn", player: 1 }),
    new Rook({ color: "white", icon: "pawn", player: 1 }),
    new Rook({ color: "white", icon: "pawn", player: 1 }),
    new Rook({ color: "white", icon: "pawn", player: 1 }),
    new Rook({ color: "white", icon: "pawn", player: 1 }),
    new Rook({ color: "white", icon: "pawn", player: 1 }),
  ],
  [
    new Rook({ color: "white", icon: "rook", player: 1 }),
    new Rook({ color: "white", icon: "knight", player: 1 }),
    new Rook({ color: "white", icon: "bishop", player: 1 }),
    new Rook({ color: "white", icon: "queen", player: 1 }),
    new Rook({ color: "white", icon: "king", player: 1 }),
    new Rook({ color: "white", icon: "bishop", player: 1 }),
    new Rook({ color: "white", icon: "knight", player: 1 }),
    new Rook({ color: "white", icon: "rook", player: 1 }),
  ],
];

console.table(board.map(row=>row)) displays this board in tabular form:

Once we have this structure, we can display it in our React Applications’s Board component:

function Board() {
  return (
    // render board logic to display board 2D-array    
  );
}

With some styling, we can manage to achieve the board display like below:

(For a working example of JavaScript inheritance and polymorphism, you can read about two player chess application in React or check the live demo)

Are There Drawbacks Of JavaScript Inheritance And Polymorphism In React JS?

One drawback of using JavaScript classes in any frontend application, including React, is that we cannot transmit data to and from backend as class objects. The transferable data is mostly pure JSON. To overcome this we have to serialize and deserialize the JavaScript class objects on sending and receiving data respectively.

Conclusion

To conclude, inheritance of React components is against the React’s philosophy. Composition is preferred and suggested in place of it. But we can and should use both inheritance and polymorphism in our frontend JavaScript code where necessary.

See also