Become a better godfather for your React components
Being sloppy when it comes about naming our React components might seem a bit insignificant at first but trust me, it definitely isn't.
Let's see an actual React
component:
export default ({ name }) => <>Hello {name}</>;
Any issues you see? Let me give you a hint:
This is how an Anonymous
component looks like in the components tree.
# Anonymous components
So yeah this component actually doesn't have a name so in React Developer Tools appears as Anonymous
. This can be a huge problem down the road for a bunch of reasons.
Here is one of them:
The more Anonymous
components we have, the less maintainable our codebase gets and that's a fact. By doing so it is actually like shooting our leg since it becomes really tough to debug our application.
Let's tackle this issue then:
const Greeting = ({ name }) => <>Hello {name}</>;
export default Greeting;
Nice!! So out of the box the name of the named function Greeting
is shown in the Developer Tools.
Ok, this definitely makes sense. So let's say we decide to become super-careful React engineers by giving names to all components of ours.
Is this enough? No, it is not since there are cases we need to be extremely cautious and tackle them separately.
# Components wrapped with React.memo have naming issues
Let's say we have a presentational component named UserInfo
that is wrapped with React.memo
for some performance optimization:
const UserInfo = React.memo(
({ user }) => <li>Hello {user.name}</li>,
(prevProps, nextProps) => (
prevProps.user.id === nextProps.user.id
),
);
export default UserInfo;
Then we can call it into action like this:
const USERS = [
{
id: 1,
name: 'John Doe',
},
{
id: 2,
name: 'Jane Doe',
},
];
const App = () => (
<ul>
{USERS.map((user) => (
<UserInfo user={user} key={user.id} />
))}
</ul>
);
export default App;
Pretty common stuff!! So what do you think? Are we going to see UserInfo
or Anonymous
in Developer Tools? It is expected to see UserInfo
, right?
Unfortunately we will see Anonymous
:
Arghhhh. The memoization applied by the higher order component React.memo
ruined the component's name. This is definitely ugly.
How can we tackle it? Shall we use displayName
to name it explicitly?
const UserInfo = React.memo(
({ user }) => <li>Hello {user.name}</li>,
(prevProps, nextProps) => (
prevProps.user.id === nextProps.user.id
),
);
UserInfo.displayName = 'UserInfo';
export default UserInfo;
Hmmm, still no luck:
Let's pull the actual function component outside of React.memo
and call it afterwards:
const UserInfo = ({ user }) => <li>Hello {user.name}</li>;
export default React.memo(
UserInfo,
(prevProps, nextProps) => (
prevProps.user.id === nextProps.user.id
),
);
It works ๐:
This means that we need to be extra cautious when it comes about React.memo
since we need to declare the component first and then pass it as a parameter to the memoization function.
Now that we proved that there are cases we need to take care of explicitly, let's check what is going on with other Higher Order functions that React
provides.
# Components wrapped with React.forwardRef have naming issues too
As we know React.forwardRef
is the way to pass a reference
down to children function components. Since React.forwardRef
is also a higher order component that wraps our functions we need to double-check its behaviour regarding components name.
Let's see an example then:
const Button = React.forwardRef(({ children }, ref) => (
<button ref={ref}>{children}</button>
));
export default Button;
So now we can simply call Button
component into action by adding a ref
. This means that we can access the inner HTML5 button
element from the parent App
component:
const App = () => {
const ref = React.useRef(null);
return <Button ref={ref}>Click me</Button>;
};
export default App;
Time to check the Developer Tools:
I am sure you saw this coming, right? Obviously the behaviour is error-prone regarding naming, same as with React.memo
.
How can we tackle this issue? I think displayName
is going to work here:
const Button = React.forwardRef(({ children }, ref) => (
<button ref={ref}>{children}</button>
));
Button.displayName = 'Button';
export default Button;
Oh, it did ๐ช
# Components profiling is pretty hard
Apart from the components tree representation when it comes about the application's profiling, things are not better.
Below we can see an example after trying to do some profiling with React Developer Tools while clicking our Button
component wrapped with React.createRef
without using displayName
:
Hmmm. It is pretty hard to figure out what is going on I suppose ๐ฌ
Same problems if we use Chrome Devtools for profiling. Components without proper naming appear as Unknown
in Chrome Devtools:
While the same components can be found a lot easier if they are given actual names:
Quite straightforward, right?
So we focused a lot on Developer Tools, debugging and profiling since these play a vital role to build and ship a robust web application with React
. Anonymous functions might cause us other issues also like making stack traces difficult to read when we are trying to spot the root of evil fast.
All these definitely affect also the Development Experience which is actually an important factor that we should never underestimate especially when we are talking about complex and big codebases where a team of developers is working on. Cheers!!
Did you like this one?
I hope you really did...
Newsletter
Get notified about latest posts and updates once a week!!
You liked it?