Skip to content
This repository was archived by the owner on Jun 26, 2020. It is now read-only.

Show proper names for stateless functional components #267

Closed
sophiebits opened this issue Oct 29, 2015 · 27 comments
Closed

Show proper names for stateless functional components #267

sophiebits opened this issue Oct 29, 2015 · 27 comments

Comments

@sophiebits
Copy link
Contributor

@bordoley says they all show up as StatelessComponent.

@jaredly
Copy link
Contributor

jaredly commented Oct 29, 2015

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

@bordoley
Copy link

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.

@steveszc
Copy link

steveszc commented Nov 2, 2015

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 />
);

@jaredly
Copy link
Contributor

jaredly commented Nov 2, 2015

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

On Mon, Nov 2, 2015 at 2:24 PM Steve Szczecina notifications@github.com
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
#267 (comment)
.

@steveszc
Copy link

steveszc commented Nov 2, 2015

I'm using Babelify 6.3.0 with Browserify

@sophiebits
Copy link
Contributor Author

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

@joaomilho
Copy link

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

@jaredly
Copy link
Contributor

jaredly commented Dec 5, 2015

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

@xixixao
Copy link

xixixao commented Dec 14, 2015

Also see facebook/react#5618

@basarat
Copy link

basarat commented Feb 11, 2016

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 🌹

@tomduncalf
Copy link

@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.

@tomduncalf
Copy link

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: wyze/babel-plugin-transform-react-stateless-component-name#2)

@tomduncalf
Copy link

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)

@deyceg
Copy link

deyceg commented Jun 21, 2016

@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

@gaearon
Copy link
Contributor

gaearon commented Aug 19, 2016

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.

@gaearon gaearon closed this as completed Aug 19, 2016
@aaronbeall
Copy link

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?

@basarat
Copy link

basarat commented Nov 9, 2016

is there a fix for this?

As mentioned before : #267 (comment) 🌹

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

@aaronbeall
Copy link

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>
  );
}

@basarat
Copy link

basarat commented Nov 9, 2016

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 🌹 ❤️

@basarat
Copy link

basarat commented Nov 9, 2016

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 🌹

@aaronbeall
Copy link

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.

@gaearon
Copy link
Contributor

gaearon commented Feb 15, 2017

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

@gaearon
Copy link
Contributor

gaearon commented Feb 15, 2017

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

@aaronbeall
Copy link

aaronbeall commented Feb 15, 2017

@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).

@Zacqary
Copy link

Zacqary commented Mar 2, 2017

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.

@gaearon
Copy link
Contributor

gaearon commented Mar 2, 2017

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;

@ravshansbox
Copy link

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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests