React: Calling super method of React.Component children

Created on 27 Feb 2015  路  15Comments  路  Source: facebook/react

I have inheritance in my app shown below. I am using babeljs transpiler (6to5), and calling super method generates an exception:

class Component extends React.Component {
  //
}

class MyComponent extend Component {
  componentDidUpdate() {
    super.componentDidUpdate(); // here error - could not call an undefined
    // do smth
  }
}

I think the easiest solution is to add empty methods in React.Component class?

Or did I do something wrong?

Wontfix

Most helpful comment

What about situation when you are adding functionality to component by extending it?

class Button extends Component {
  componentDidMount() {
    // Some logic to setup listeners etc...
  }
}

class LightButton extends Button {
  componentDidMount() {
     // ??? How to make execute method componentDidMount of Button ???
     // Additional logic to add even more listeners 
  }
}

And let's say Button if from 3rd party library for which code can not be modified.

All 15 comments

Since your base class doesn't define them, they're not there so I think that's indeed expected.
The real issue, in my opinion, is that you're using inheritance for React components.

It's a bad pattern in my book, and Jordan explains why:

Many people have become accustomed to using OO inheritance not just as a tool, but as the primary means of abstraction in their application. I've you've worked at a Java shop, you'll know what I'm talking about. In my personal opinion, classical OO inheritance (as implemented in many popular languages) is not often the best tool for most jobs, let alone all jobs. But the situation should be approached with even more caution when inheritance is used within a framework or paradigm that uses functional composition as its primary abstraction (React). There are certain patterns we'll want to prevent (there are many strange things that people can come up with when combining render with inheritance that don't make sense and are addressed via simple composition). There's also risk of making mutation more convenient. It might make sense to start with ES6 classes simply as a better syntax for React component creation, intentionally limiting some use cases (limiting inheritance depth, making some React base class methods final) (only when used with React components of course - all non-React use of ES6 classes wouldn't be restricted).

What's your use case? Are you absolutely sure you can't do the same with simple composition (something React is powerful at)?

Offcourse you can do all things with simple composition in Javascript. But abstraction and other cool OO stuff do things easier, for me though.

Offcourse you can do all things with simple composition in Javascript.

I mean that React in particular provides a very good composition model. Instead of

class Button extends Component {
  getColor() {
    return this.props.color;
  }
  render() {
    return <div style={{ color: this.getColor() }}>{this.props.children}</div>;
  }
}

class LightButton extends Button {
  getColor() {
    return lighten(super.getColor(), 0.2);
  }
}

You can write

class Button extends Component {
  render() {
    return <div style={{ color: this.props.color }}>{this.props.children}</div>;
  }
}

class LightButton extends Component {
  render() {
    return <Button color={lighten(this.props.color, 0.2)}>{this.props.children}</Button>;
  }
}

This is why we don鈥檛 encourage inheritance in components.

I鈥檓 closing as we are unlikely to add special conveniences to make inheritance easier, as it is an anti-pattern with a good migration way I described above.

Please let me know if there is something that inheritance gives you but composition doesn鈥檛. I鈥檇 love know more about why you chose to use inheritance for React components instead. Thanks!

What about hoc, like in the example here http://stackoverflow.com/questions/41400357/super-render-causes-error-unable-to-get-call-of-undefined-or-null-reference. Calling super in this example seems to work and is in fact the whole benefit of doing this. Am I missing something?

I think it's because super in that case points to WrappedComponent rather than React.Component, but I'm not sure 馃槉

Calling super in this example seems to work and is in fact the whole benefit of doing this.

Sure but we don鈥檛 recommend this pattern anywhere.
This is also not what is traditionally called a HOC.

You technically can do it but it鈥檚 rarely a good idea in our experience.

Read more here: https://facebook.github.io/react/docs/composition-vs-inheritance.html

What about situation when you are adding functionality to component by extending it?

class Button extends Component {
  componentDidMount() {
    // Some logic to setup listeners etc...
  }
}

class LightButton extends Button {
  componentDidMount() {
     // ??? How to make execute method componentDidMount of Button ???
     // Additional logic to add even more listeners 
  }
}

And let's say Button if from 3rd party library for which code can not be modified.

You should not extend class Components at all @vincnetas, and instead you should wrap the original Button in a HOC and then overload the existing componentDidMount() by defining your own version of it.

More Info:HOC
More Info: Egghead.io

How about:

class ShouldNotUpdate extends React.PureComponent {

    shouldComponentUpdate(nextProps, nextState) {
        return foo || bar || super.shouldComponentUpdate(nextProps, nextState);
    }

    render() {
                // ...
    }
}

I'm puzzled why super doesn't work inside React component

@vincnetas what about intercepting lifecycle methods?

for example I want to measure the time occurred between componentWillMount and componentDidMount of the child component? How can I achieve that with a HOC?

I'm with @alamothe, I'm looking for shouldComponentUpdate to return false for some particular cases, but fall back onto the the React.Component (or pureComponent).

I know there is a way to refactor my app to do it in a better way, but as it is it'd be easier in the mean time to optimise a few known conditions that spam updates.

I've used a workaround solution (do not consider it as a best practice)

class Button extends Component {
  _componentDidMount() {
    // Some logic to setup listeners etc...
  }
}

class LightButton extends Button {
  componentDidMount() {
     this._componentDidMount();
     // Additional logic to add even more listeners 
  }
}

@alamothe I am with you! I am trying to debug a case where a component re-renders where it shouldn't and being able to tap into those hooks is important for that

@alamothe

class ShouldNotUpdateInner extends React.PureComponent {
  render() {
    // ...
  }
}

class ShouldNotUpdate extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    return foo || bar;
  }

  render() {
    return <ShouldNotUpdateInner {...this.props} />;
  }
}

Please let me know if there is something that inheritance gives you but composition doesn鈥檛. I鈥檇 love know more about why you chose to use inheritance for React components instead. Thanks!

@gaearon wouldn't something like this be pretty hard to achieve using composition over inheritance?

import { ErrorBoundary } from 'react-error-boundary';

class ReactRouterErrorBoundary extends ErrorBoundary {
  componentDidMount() {
    this.props.history.listen((location, action) => {
      if (this.state.error) {
        this.resetErrorBoundary();
      }
    });
  }
}

export default withRouter(ReactRouterErrorBoundary);

If for example you want to add support for react-router with a React class component in a third party npm package such as react-error-boundary.

I prefer functional style programming over OOP/imperative, so of course you should use functional components and hooks over classes and HOCs - and composition whenever possible, but I just can't deny how easy this is. Would love to know if there's a better way where I can avoid inheritance, without having to rewrite the component.

Was this page helpful?
0 / 5 - 0 ratings