I ruined my React components by using optional props

lico

SeongKuk Han

Posted on December 20, 2022

I ruined my React components by using optional props

I ruined my React components by using optional props

When I made components, I was trying to make components as flexible as they could using optional parameters. But I found out that it was absolutely wrong.

There were too many options and I wasn't sure what I was going to use to make the component work properly.

I also used unexpected default values a lot. It makes plenty of unnecessary defensive code and I couldn't expect what default values it has.

I will show you some examples that are similar to the mistakes that I made.


Let's say, there is a Button component like below.

import { ReactNode } from 'react';

type ButtonType = 'primary' | 'secondary';

interface ButtonProps {
  children?: ReactNode | string;
  onClick?: VoidFunction;
  type?: ButtonType;
}

const Button = ({ children, onClick, type = 'primary' }: ButtonProps) => {
  const bgColor = type === 'primary' ? 'red' : 'blue';

  return (
    <button style={{ backgroundColor: bgColor }} onClick={onClick}>
      {children}
    </button>
  );
};

export default Button;
Enter fullscreen mode Exit fullscreen mode

Let's see if the optional props are used properly.

  • children, onClick: All we know button can be used without text and click event.
  • type: There are two options primary and secondary. We can expect from the name that primary would be used if we don't pass the prop.
import { ReactNode } from 'react';

type ColorButtonColor = 'red' | 'blue';

interface ColorButtonProps {
  children?: ReactNode | string;
  onClick?: VoidFunction;
  color?: ColorButtonColor;
}

const ColorButton = ({
  children,
  onClick,
  color = 'red',
}: ColorButtonProps) => {
  return (
    <button style={{ backgroundColor: color }} onClick={onClick}>
      {children}
    </button>
  );
};

export default ColorButton;
Enter fullscreen mode Exit fullscreen mode

This button has the color prop instead of type.
color has red as a default value.

The button might've had the default value for a purpose. For example, red might've been the most used.

But no one can expect that. In this case, it would be good that color does not have a default value.


Here is another example.

interface ListProps {
  items?: string[];
}

const List = ({ items = [] }: ListProps) => {
  return (
    <ul>
      {items.map((item, itemIdx) => (
        <li key={itemIdx}>{item}</li>
      ))}
    </ul>
  );
};

export default List;
Enter fullscreen mode Exit fullscreen mode

We can know List is a general-purposed component.

<List />
Enter fullscreen mode Exit fullscreen mode

You render it and If there is nothing shown, you would know if you forget passing a prop.

Look at another list component.

type UserListItem = {
  nickname: string;
  intro: string;
};

interface UserListProps {
  users?: UserListItem[];
}

const UserList = ({ users = [] }: UserListProps) => {
  return (
    <ul>
      {users.map((user, userIdx) => (
        <li key={userIdx}>{`${user.nickname} | ${user.intro}`}</li>
      ))}
    </ul>
  );
};

export default UserList;
Enter fullscreen mode Exit fullscreen mode

It's the same as the previous one without the type of item.

<UserList />
Enter fullscreen mode Exit fullscreen mode

If it doesn't have an error, can you know what it is wrong out of the box?

Someone may think UserList has a business logic and it displays the user list itself.

But it needs the prop users to display the user list.
If users is not optional, you must be able to know what is wrong and it would be going to save you time.


When it comes to function properties, it's also the same.

interface NavbarProps {
  onSetting: VoidFunction;
  onProfile: VoidFunction;
  onHome: VoidFunction;
}

const CornerNavbar = ({
  onSetting,
  onProfile,
  onHome,
}: NavbarProps) => {
  return (
    <ul>
      <li onClick={onSetting}>Setting</li>
      <li onClick={onProfile}>Profile</li>
      <li onClick={onHome}>Home</li>
    </ul>
  );
};

export default CornerNavbar;
Enter fullscreen mode Exit fullscreen mode

CornerNavbar has function properties and without these functions, the component wouldn't work as intended. You shouldn't make them optional.

If you defer to implementing some properties, you can use comments and temporary functions.

We can use HTML tags without passing event functions like onClick, onLoad, onMouseDown. I think the fact was confusing me.


Conclusion

These might be considered beginner mistakes and some of you might have your rules to cover this situation other than what I explained.

Although I have been using typescript, I was getting rid of an advantage of it myself. My opinion that I said in this post could be wrong though, I have learned from it that it is important to keep thinking about what I am used to.

I hope you found something useful in my article.

Happy Coding!

💖 💪 🙅 🚩
lico
SeongKuk Han

Posted on December 20, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related