Is it a good practice to create base components and then extend them in React?

tuks picture tuks · Feb 9, 2016 · Viewed 9.5k times · Source

I'm just learning React and I am writing components using ES7 syntax. My idea is to create a base component which will contain some methods that I want all derived components to have. For example, I want to implement valueLink without the mixin, for two-way binding in all my components. Here is my idea:

class MyComponent extends React.Component {

    bindTwoWay(name) {
        return {
            value: this.state[name],
            requestChange: (value) => {
                this.setState({[name]: value})
            }
        }
    };
}

 

class TextBox extends MyComponent {
    state = {
        val: ''
    };

    render() {
        return (<div>
            <input valueLink={this.bindTwoWay('val')}/>
            <div>You typed: {this.state.val}</div>
        </div>)
    }
}

And it works just fine. However, I wasn't able to find much information about this method. It's not about valueLink, that was just an example. The idea is to have some methods in a base component and later extend that component so that derived components have all those methods, like the usual OOP way. So I would just like to know if this is perfectly fine or there are some flaws that I am not aware of. Thanks.

Answer

Jarno Lonardi picture Jarno Lonardi · Feb 9, 2016

This is totally fine, it is just basic inheritance. The caveat of using inheritance to replace mixins is that there is no multiple inheritance, so you cannot give your TextBox features of multiple base classes as you would when you give it multiple mixins. To get around this you can use component composition. In your case composition would not work directly as you defined in the example but there is an workaround to this.

First you need to define a composing component

export default (ComposedComponent) => {
  class MyComponent extends React.Component {
    constructor(props, state) {
      super(props, state);
      this.state = {
        val: ''
      };
    }
    bindTwoWay(name) {
      return {
        value: this.state[name],
        requestChange: (value) => {
            this.setState({[name]: value})
        }
      };
    }

    render() {
      return (
        <ComposedComponent 
          {...this.props}
          {...this.state} 
          bindTwoWay={this.bindTwoWay.bind(this)}
        />
      }
    }
  }

  return MyComponent
}

And then you define your component where you need some common features

import compose from 'path-to-composer';

class TextBox extends React.Component {
  render() {
    return (
      <div>
        <input valueLink={this.props.bindTwoWay('val')}/>
        <div>You typed: {this.props.val}</div>
      </div>
    )
  }
}

export default compose(TextBox)

I have not tested this but it should work.