Become a better godfather for your React components

ยท 3 mins read ยท ย React

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:

Become a better godfather for your React components

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:

Become a better godfather for your React components

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.

Become a better godfather for your React components

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:

Become a better godfather for your React components

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:

Become a better godfather for your React components

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 ๐ŸŽ‰:

Become a better godfather for your React components

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:

Become a better godfather for your React components

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 ๐Ÿ’ช

Become a better godfather for your React components

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

Become a better godfather for your React components

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:

Become a better godfather for your React components

While the same components can be found a lot easier if they are given actual names:

Become a better godfather for your React components

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!!

Newsletter

Get notified about latest posts and updates once a week!!