React-devtools: Show proper names for stateless functional components

Created on 29 Oct 2015  ·  27Comments  ·  Source: facebook/react-devtools

@bordoley says they all show up as StatelessComponent.

Most helpful comment

For those using TypeScript you can do the following (example notice displayName):

declare global {
    interface Function {
        displayName: string;
    }
}

/********
 *
 * Primitives
 *
 ********/


interface PrimitiveProps extends React.HTMLProps<HTMLDivElement>{};

/**
 * Takes as much space as it needs, no more, no less
 */
export const Content = Radium((props: PrimitiveProps) => {
    const style = csx.extend(props.style || {},csx.content);
    return (
        <div data-comment="Content" {...props} style={style}>
            {props.children}
        </div>
    );
});
Content.displayName = "Content";

Sample project work in progress :rose:

All 27 comments

if you export default ({name}) => <div>{name}</div>, it's not going to have a name, but that's on babel.
However, this works fine:

const MyStateless = ({name}) => <div>{name}</div>

    .... somewhere
    <MyStateless name="Sophia" />

image

Internally (Facebook) I was seeing.

<StatelessComponent>
  <div>some text</div>
</StatelessComponent>

I'll generate a repro using public react to see if this is FB specific or not.

I am seeing this issue as well using public react.
My root component is a stateless component, if I run a production Browserify build (NODE_ENV:"production") with no mangling I get the following in React DevTools:

reactcomponent

Here is my code for that same root component:

var MyRootComponent = () => (
    <MyChildComponent />
);

What's your babel version? This is a babel issue I'm sure

On Mon, Nov 2, 2015 at 2:24 PM Steve Szczecina [email protected]
wrote:

I am seeing this issue as well using public react.
My root component is a stateless component, if I run a production
Browserify build (NODE_ENV:"production") with no mangling I get the
following in React DevTools:

[image: statelesscomponent]
https://cloud.githubusercontent.com/assets/6877934/10894645/32f1fd9e-816d-11e5-8615-564e91edc3d0.png

Here is my code for that same root component:

var MyRootComponent = () => (

);


Reply to this email directly or view it on GitHub
https://github.com/facebook/react-devtools/issues/267#issuecomment-153161776
.

I'm using Babelify 6.3.0 with Browserify

Maybe you don't have the function.name transform enabled? We probably rely on that.

Using browserify with coffee-reactify. Anything that I can do?

@joaomilho make an issue w/ coffee-reactify asking them about getting displayNames enabled

For those using TypeScript you can do the following (example notice displayName):

declare global {
    interface Function {
        displayName: string;
    }
}

/********
 *
 * Primitives
 *
 ********/


interface PrimitiveProps extends React.HTMLProps<HTMLDivElement>{};

/**
 * Takes as much space as it needs, no more, no less
 */
export const Content = Radium((props: PrimitiveProps) => {
    const style = csx.extend(props.style || {},csx.content);
    return (
        <div data-comment="Content" {...props} style={style}>
            {props.children}
        </div>
    );
});
Content.displayName = "Content";

Sample project work in progress :rose:

@basarat Old comment I know but hopefully this is useful for someone:

It's possible to get this working automatically using this Babel plugin which will use the filename to assign the function a name if it does not have one: https://github.com/wyze/babel-plugin-transform-react-stateless-component-name/

Few things to watch out for:

  • ensure it comes before the "react-hot-loader/babel" plugin in your .babelrc
  • for it to work with Typescript, you need to be using Babel as part of your build (obviously – e.g. running babel-loader after ts-loader for all your *.ts* files) and you need to have module: "es6" and jsx: "preserve" in your tsconfig.json or the plugin won't recognise that the components are actually stateless ones

This doesn't currently work (either in ES6 or Typescript) for SFCs wrapped in a higher order component, e.g. observe from MobX. I'll raise an issue on the project for this.

Oh, or even easier, you can do away with the plugin and just name your component by putting into a constant, then exporting that separately, for example:

const MyComponent = () => <div>Hi</div>

export default MyComponent

Will show as MyComponent in dev tools (with Typescript again, you need to set the module option appropriately I think, although probably not jsx).

Slightly more typing but probably clearer. Note this still doesn't seem to solve the HOC problem (already raised at: https://github.com/wyze/babel-plugin-transform-react-stateless-component-name/issues/2)

Ah (sorry to spam this thread!) – there is a way round the HOC problem, which is to do as the above and store the component in a named constant, then wrap it when exporting (rather than when creating), so instead of:

const MyComponent = observer(() => <div>Hi</div>)
export default MyComponent

You have:

const MyComponent = () => <div>Hi</div>
export default observer(MyComponent)

Which arguably reads better anyway :) (and allows you to export the non-decorated component if needed e.g. for testing)

@tomduncalf Just ran into this problem myself!

const { name } = element.type;
if (name && name.endsWith('Control')) {...}

Your fix worked until I turned mangling on with uglifyjs

Closing.

If you use Babel, es2015 preset contains function-name plugin that infers the name based on left hand side in expressions.

For Facebook internal use case, @spicyj recently fixed it by enabling the same transform.

I'm using TypeScript with:

export const MyComponent = ({text}) => (
  <div>{text}</div>
);

And I just see <StatelessComponent> in the dev tools... is there a fix for this?

is there a fix for this?

As mentioned before : https://github.com/facebook/react-devtools/issues/267#issuecomment-182658039 :rose:

declare global {
    interface Function {
        displayName?: string;
    }
}
export const MyComponent = ({text}) => (
  <div>{text}</div>
);
MyComponent.displayName = 'MyComponent';

Thanks that does work... I suppose I was asking if there's a way to make it work without explicitly writing the displayName (which means you have to write the name twice).

I found that writing it this way seems to work:

function MyComponent({text}) {
  return (
    <div>{text}</div>
  );
}

I found that writing it this way seems to work:

Yup, on modern browsers, cause they have .name automatically. I'll use that myself as well from now on :rose: :heart:

On second thought both are equivalent on chrome as it also adds .name for arrow assignments to variables e.g. run the following on the console

const x = ()=> undefined; function y(){}; console.log(x.name,y.name); // x y 

export might be throwing the emit (and/or) the v8's name detection off for const :rose:

I think you're right about the export confusing something. If I do this:

export const MyComponent = () => { };

I get <StatelessComponent> in the dev tools.

But if I do this:

const MyComponent = () => { };
export {MyComponent};

I get <MyComponent> in the dev tools as expected.

@aaronbeall Please file a bug with Babel if Babel doesn't add implicit name to arrow functions when they're used as exports.

(Or with TypeScript if that's what you use)

@gaearon Yep I'm using TypeScript. It doesn't look like TS emits a .name in any case... but I guess browsers are able to assign a name to functions when they are assigned to a var but not when they are assigned to a prop of an exports object? This example shows how TS emits to JS and every component name appears in dev-tools except export const Name = () => (the one I now use most, of course, lol).

Just stumbled on this thread. I've been running into an issue where the React Dev Tools tell me my component is called <Unknown> whenever I do:

// MyComponent.js
export default (props) => <someJSX>;
// App.js
import MyComponent from './MyComponent.js';

But this works:

export default function MyComponent(props) {
    return <someJSX>;
}

In case that's useful for anyone.

Yes, this is expected, because Babel only infers the arrow name if you assign it to something.
For example, this would also work:

const MyComponent = (props) => <someJSX />;
export default MyComponent;

I could fix the problem in typescript project by setting module flag to es2015 and relying on webpacks native support for es modules. You have to set moduleResolution to node as well.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bvaughn picture bvaughn  ·  43Comments

ogawa0071 picture ogawa0071  ·  28Comments

gaearon picture gaearon  ·  29Comments

erichdev picture erichdev  ·  26Comments

gaearon picture gaearon  ·  20Comments