facebook / react-devtools

An extension that allows inspection of React component hierarchy in the Chrome and Firefox Developer Tools.
Other
11.04k stars 1.78k forks source link

Show proper names for stateless functional components #267

Closed sophiebits closed 8 years ago

sophiebits commented 9 years ago

@bordoley says they all show up as StatelessComponent.

jaredly commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 https://github.com/facebook/react-devtools/issues/267#issuecomment-153161776 .

steveszc commented 8 years ago

I'm using Babelify 6.3.0 with Browserify

sophiebits commented 8 years ago

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

joaomilho commented 8 years ago

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

jaredly commented 8 years ago

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

xixixao commented 8 years ago

Also see https://github.com/facebook/react/issues/5618

basarat commented 8 years ago

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:

tomduncalf commented 8 years ago

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

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 commented 8 years ago

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)

tomduncalf commented 8 years ago

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 commented 8 years ago

@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 commented 8 years ago

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.

aaronbeall commented 7 years ago

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 commented 7 years ago

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';
aaronbeall commented 7 years ago

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 commented 7 years ago

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:

basarat commented 7 years ago

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:

aaronbeall commented 7 years ago

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 commented 7 years ago

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

gaearon commented 7 years ago

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

aaronbeall commented 7 years ago

@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](http://www.typescriptlang.org/play/#src=const%20ArrowFunction%20%3D%20()%20%3D%3E%20%7B%20%7D%3B%0D%0Afunction%20NamedFunction()%20%7B%20%7D%0D%0A%0D%0Aexport%20const%20ExportedArrowFunction%20%3D%20()%20%3D%3E%20%7B%20%7D%3B%20%2F%2F%20Shows%20%3CUnknown%3E%20in%20dev-tools%20(all%20others%20work)%0D%0Aexport%20function%20ExportedNamedFunction()%20%7B%20%7D%0D%0A%0D%0Aconst%20ExportedArrowFunctionOnObject%20%3D%20()%20%3D%3E%20%7B%20%7D%3B%0D%0Aexport%20%7B%20ExportedArrowFunctionOnObject%20%7D) 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 commented 7 years ago

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 commented 7 years ago

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 commented 6 years ago

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.