DEV Community

Discussion on: Conditionally render react components in cleaner way

wintercounter profile image
Victor Vincent

That it needs an extra tool (TS) to make it somewhat useable imo already validates my concern. You still cannot SEE from the code what it is.

Why is it so obvious that all components will receive the same prop always? Even in the example there is the logged out case which makes this invalid. Sure, it works fine until I need more/different props, but why would I settle down with a solution that doesn't let me modify later? Especially when the other solution doesn't cost me any extra effort over the enum one.

Thread Thread
lukeshiru profile image
LUKESHIRU • Edited on

Even in the example there is the logged out case which makes this invalid.

I used DefaultProfile instead, but ok...

import { FC } from "react";

type ProfileProps = JSX.IntrinsicElements["div"];
type ProfileComponent = FC<Props>;

const AdminProfile: ProfileComponent = props => <div {...props} />;
const UserProfile: ProfileComponent = props => <div {...props} />;
const LoggedOut: ProfileComponent = props => <div {...props}>Login to see your profile</div>;

const roleComponentMap = {
  admin: AdminProfile,
  user: UserProfile,
  default: LoggedOut
};

export const Profile: FC<ProfileProps & {
  readonly role?: keyof typeof roleComponentMap;
}> = ({ role = "default", ...props }) => {
  const Component = roleComponentMap[role];

  return <Component {...props} />;
};
Enter fullscreen mode Exit fullscreen mode

but why would I settle down with a solution that doesn't let me modify later?

I agree, solutions should take the future into consideration, but what in the code above makes you think you can't modify it later? Let's say now I want to add a new property "color" to Profile, that will set the classname of the rendered profile component to be color-${color}, with the above approach is quite easy:

export const Profile: FC<ProfileProps & {
  readonly role?: keyof typeof roleComponentMap;
  readonly color?: string;
}> = ({ className, color, role = "default", ...props }) => {
  const Component = roleComponentMap[role];

  return <Component className={color ? ` color-${color}` : undefined} {...props} />;
};
Enter fullscreen mode Exit fullscreen mode

With the switch/case, to achieve the same, I have to actually...

const Settings = ({ color }) => {
    const { role } = useContext(SessionContext)
    const className = color ? `color-${color}` : undefined;

    switch (role) {
        case 'admin': return <AdminSettings className={className} />
        case 'user': return <UserSettings className={className} />
        default: return <LoggedOut className={className}>Login to see your profile</LoggedOut>
    }
}
Enter fullscreen mode Exit fullscreen mode

The "initial" cost is pretty much the same, but the long term cost of the switch/case in this scenario is higher. When you're wrapping different components inside a single component (which is the case for this component), is better to share props across them because they will be used in the same places, so ideally they should receive the same props. They might ignore some of them, and use others.