I have been working with React for just about 2 years now, comprising about 95% of my professional work. Most issues are easy to find solutions to but one issue I see people have that does not seem to have a lot of documentation is cases of <Unknown>
components when using React DevTools.
Generally speaking, React will infer the name of a component based on the function or class name provided. Most of the time, this is all you need to know and you shouldn't run in to any problems. However, there are a few ways to create React components that could lead to issues. I've put together some common examples and how they will appear in React DevTools.
I'll be using ES6 syntax throughout this post.
Class Components
Class components are pretty straight forward. I think most people tend to name their classes before exporting them.
// MyClassComp.js
class MyClassComp extends React.Component {
render() {
return <div> Hello World! </div>;
};
}
export default MyClassComp;
// App.js
import MyClassComp from './MyClassComp';
class App extends React.Component {
render() {
return (
<div className="App">
<MyClassComp />
</div>
);
}
}
No real surprise here, React can easily decipher the name of your component. What if we export the class directly?
// MyClassComp.js
export default class extends React.Component {
render() {
return <div> Hello World! </div>;
};
}
// App.js
import MyClassComp from './MyClassComp';
class App extends React.Component {
render() {
return (
<div className="App">
<MyClassComp />
</div>
);
}
}
Well, not quite an <Unknown>
but still not very helpful information.
Functional Components
Functional components will generally work the same as class components. However, it is easier to create and export functional components in a variety of ways, so you have to be a bit more cognizant.
Here is an example of a named export and a default export.
// MyDefaultComp.js
export const MyNamedComp = props => <div> Named Export! </div>;
const MyDefaultComp = props => <div> Default Export! </div>;
export default MyDefaultComp;
// App.js
import MyDefaultComp, {MyNamedComp} from './MyDefaultComp';
class App extends React.Component {
render() {
return (
<div className="App">
<MyNamedComp />
<MyDefaultComp />
</div>
);
}
}
As you can see, both are named correctly in DevTools. But what if we were to export an anonymous function?
// MyFunctionalComp.js
export default props => <div> Hello World! </div>;
// App.js
import MyFunctionalComp from './MyFunctionalComp';
class App extends React.Component {
render() {
return (
<div className="App">
<MyFunctionalComp />
</div>
);
}
}
I don't think it is much of a shock that DevTools now gives an <Unknown>
component. We didn't provide a name to the component when exporting it so React can't infer it for us.
Higher-Order Components
Higher-order components are a bit more complicated. Here is higher-order that takes a component as an argument and renders it.
// HigherOrderComp.js
const HigherOrderComp = Wrapped => props => <Wrapped />;
export default HigherOrderComp;
// App.js
import HigherOrder from './HigherOrder';
const MyComponent = props => <div> Hello World! </div>;
const MyHOC = HigherOrder(MyComponent);
class App extends React.Component {
render() {
return (
<div className="App">
<MyHOC />
</div>
);
}
}
Interesting! Despite defining a name for our higher-order component, the name gets lost in the DevTools. Also, the wrapped component retains its name!
Render Props
One last example of a more advanced technique using render props.
// RenderPropComp.js
class RenderPropComp extends React.Component {
render() {
const { Renderer } = this.props
return <Renderer />;
}
}
export default RenderPropComp;
// App.js
import TakesRenderer from './TakesRenderer';
const MyComponent = props => <div> Hello World! </div>;
class App extends React.Component {
render() {
return (
<div className="App">
<TakesRenderer
Renderer={MyComponent}
/>
</div>
);
}
}
Great, that preserves the names of both components!
The biggest take away is providing an explicit name when you define/export a React component can go a long way to making debugging easier. Personally, each component I create goes in a separate file, with a specific name, and an explicit export. In the case of composing components, I prefer the render props pattern over using higher-order components.
These are a few simple examples but I think they are worth considering. It can be very confusing looking through the React DevTools when it is a sea of <Unknown>
. Considering these patterns could make your React debugging much easier.
Top comments (3)
To clarify some points.
The problem isn't with "exporting component directly". The problem is that you didn't give it a name.
Your code looks like this:
The class doesn't have a name. If you give it a name this won't be a problem (even though you still "export it directly"):
This isn't some problem inherent to HOCs themselves. The problem is in how HOC is defined. If it doesn't a class/function with a name... well, you know the rest. :-)
So you can totally write a HOC that adds a reasonable name, just follow the same suggestions you already wrote, but for the returned class itself.
Dan,
Along with trying to help others, this post is just as much for me to see how well I understand a topic. I really appreciate that you took the time to read my post and offer up such helpful feedback because it not only improves the post, but it improves my knowledge on the topic.
I follow a lot of topics around React and Redux, so having you personally respond to this post blows my mind! Something I wouldn't have ever expected, so thank you again!
How I write a HOC so that names are preserved in Devtools:
My HOC will now show up in Devtools like
withWrapperComponent
We don't have to use anonymous functions all the time