facebook/react-devtools

ES6 Components that extend React.Component are always shown as the base class

Closed this issue · 15 comments

// The base class
class ViewComponent extends React.Component { }

// The react components that are used.
class Header extends ViewComponent { render() {} }
class Content extends ViewComponent { render() {} }

Now both <Header> and <Content> are shown in React Developer Tools as ViewComponent. This makes the whole DevTools quite useless when you have a base class for all components inherit from, since everything that's shown in DevTools are ViewComponents.

What is shown is the 'displayName' of your component. I assume Babel adds a displayName automatically to subclasses of React.Component, but not to other classes. You can add a displayName manually to your classes, or come up w/ a babel plugin that will add a displayName to all classes.
As an aside, I suggest against inheritance in general :) composition + class factories serve me very well.

jimfb commented

@prasannavl Hidden deep in the comments of facebook/react#5618 (comment) is a mention of the fact that we should display the element name in addition to the class displayName (this is something we want to do). I know this isn't a clean solution to your original issue, but it might mitigate your issue.

I am probably not technical enough to really help, but when I had my code setup using ES6 classes every tag was shown as T, changing the code back to var Name = React.createClass({}) fixed the react dev tools.

@prasannavl : I have been working around this issue by setting the name to a static property called "displayName" based on @jimfb 's input.

I'm pretty new to this whole ecosystem, but I was curious if this is the same reason that decorators don't seem to work. See example: http://gaearon.github.io/react-dnd/examples-chessboard-tutorial-app.html or is that a separate issue?

the same reason that decorators don't seem to work

Can you clarify what doesn’t work on that page?
I see both the wrapping and the wrapped components:

screen shot 2016-05-18 at 23 12 41

a there is called this way because it is from minified code.

@gaearon Doh. I'm dumb. Thanks for the insights!

Finally I figured out why I keep getting error messages about the wrong component!
If all your components extend from a base class as @prasannavl mentioned, it will be obvious that something is wrong. But if you mostly extend from Component and you only have some classes extend each other, then it will be not that obvious at all and the error message can be real confusing!

So I tried what happened if I just delete the displayName from the component class after it has been created:

export class BaseComponent extends Component { /* ... */ }
delete BaseComponent.displayName;
export class DerivedComponent extends BaseComponent { /* ... */ }

This works!

Both in error messages as well as in the React dev tools, the name of the component is correct down the whole class hierarchy.

I think the displayName feature is ok as it is, but that Babel should actually not add that property for regular ES6 classes extending Component as React can already get the name from the class name.

@Download Note that inheritance is severely discouraged in React.

@gaearon Any motivations for that? It seems to be working just fine except for this issue.

@Download

It’s just unnecessary because React already has a strong composition model. Inheritance hierarchies generally don’t scale well to complex UIs.

Here’s an example of solving a problem with inheritance:

import { Component } from 'react';

class BaseButton extends Component {
  componentDidMount() {
    console.log('hey');
  }

  render() {
    return <button>{this.getContent()}</button>;
  }
}

class RedButton extends BaseButton {
  componentDidMount() {
    super.componentDidMount();
    console.log('ho');
  }

  getContent() {
    return 'I am red';
  }

  render() {
    return (
      <div className='red'>
        {super.render()}
      </div>
    );
  }
}

In our experience this is hard to maintain.
This is also not how we intend React to be used.

In React you can express the same by passing props:

import { Component } from 'react';

class Button extends Component {
  componentDidMount() {
    console.log('hey');
  }

  render() {
    return (
      <div className={this.props.color}>
        <button>{this.props.children}</button>
      </div>
    );
  }
}

class RedButton extends Component {
  componentDidMount() {
    console.log('ho');
  }

  render() {
    return (
      <Button color='red'>
        I am red
      </Button>
    );
  }
}

In our experience this is simpler and more flexible because you don’t have the “fragile base class” problem.

@gaearon Thanks for the explanation 👍

Going to close this, but let me know if you still have issues.

There is an edge case you might hit (base class has displayName but descendant only has a name). We could conceivably check if displayName is own property, and if it's not but name is available, prefer name. If you're interested in implementing this, please send a PR!

I have similar issue when passing class instance to component's props.

In short, I'm doing smth like this:

class A { ...whatever... }

class B extends A {
  constructor() {
    super()
    this.some = {}
  }

  get foo() {
    return this.some.value
  }
}

<MyComponent the_instance={new B()} />

Then, devtools shows me correct info:

screenshot 2019-01-15 11 56 16

When accessing in console $r.props.the_instance.foo, it also works normal:

screenshot 2019-01-15 11 57 28

BUT when expanding the_instance prop in devtool's sidebar, smth strange happens:

screenshot 2019-01-15 11 56 55

Now it's an instance of not B but A, and obviously crashes because can't read property of undefined.

Not fatal, but annoying thing, which complicates debugging.


My .babelrc, if it can help:

{
  "presets": [
    [ "env", {
      "targets": {
        "browsers": [
          "last 2 versions"
        ]
      },
      "useBuiltIns": true
    }],

    "react"
  ],

  "plugins": [
    "syntax-dynamic-import",
    "transform-object-rest-spread",

    [ "transform-runtime", { "polyfill": false } ],

    [ "transform-builtin-extend", {
      "globals": [ "Error" ]
    }],

    "transform-decorators-legacy",
    "transform-class-properties",
    "transform-async-generator-functions"
  ],

  "env": {
    "development": {
      "plugins": [
        "transform-react-jsx-source"
      ]
    }
  }
}

@jeron-diovis

I think you have hit an actual issue. But I also think it would be better for you to create a new issue and just reference this one. I am still watching this issue because I once posted here, but many maintainers will never see your issue if you add it to a closed topic. And as I think you really hit something here, it should be ok to just create a new issue for it. You can just add a hash sign and the issue number of this issue to your new issue to refer people back here for background info. Like this:

#308: #308

My € 0,02.