Render react components conditionally in the most readable way possible
<Switch expression={state}>
<Case value={1}>I'll be rendered if state is 1</Case>
<Case value={2}>I'll be rendered if state is 2</Case>
<Case value={3}>I'll be rendered if state is 3</Case>
<Default>I'll be rendered if state isn't 1, 2, or 3</Default>
</Switch>
import {
Children,
isValidElement,
useEffect,
useRef,
type ReactElement,
type FC,
type PropsWithChildren,
} from "react";
type ISwitch = FC<PropsWithChildren<{ expression?: unknown }>>;
type ICase = FC<PropsWithChildren<{ value?: unknown }>>;
const Case: ICase = ({ children }) => <>{children}</>;
const Default: FC<PropsWithChildren> = ({ children }) => <>{children}</>;
const Switch: ISwitch = ({ children, expression }) => {
const matchingCaseRef = useRef<ReactElement>();
const defaultCaseRef = useRef<ReactElement>();
useEffect(() => {
matchingCaseRef.current = undefined;
defaultCaseRef.current = undefined;
}, [children]);
if (!children) return null;
Children.forEach(children, (child) => {
if (!isValidElement(child)) return;
if (!matchingCaseRef.current && child.type === Case) {
const conditionResult =
Boolean(expression) ===
Boolean((child.props as { value: string }).value);
if (conditionResult) matchingCaseRef.current = child;
} else if (!defaultCaseRef.current && child.type === Default)
defaultCaseRef.current = child;
});
return matchingCaseRef.current ?? defaultCaseRef.current ?? null;
};
import { render, screen } from "@testing-library/react";
import { Switch, Case, Default } from "./SwitchCase";
describe("Switch Kit", () => {
it("renders first matching case", () => {
render(
<Switch expression={true}>
<Case value={true}>Case 1</Case>
<Case value={true}>Case 2</Case>
</Switch>
);
expect(() => screen.getByText("Case 1")).not.toThrow();
expect(() => screen.getByText("Case 2")).toThrow();
});
it("renders the correct matching case", () => {
render(
<Switch expression={true}>
<Case value={false}>Case 3</Case>
<Case value={true}>Case 4</Case>
</Switch>
);
expect(() => screen.getByText("Case 3")).toThrow();
expect(() => screen.getByText("Case 4")).not.toThrow();
});
it("renders the default case if there is no matching case provided", () => {
render(
<Switch expression={true}>
<Case value={false}>Falsy Case</Case>
<Default>Default Case</Default>
</Switch>
);
expect(() => screen.getByText("Falsy Case")).toThrow();
expect(() => screen.getByText("Default Case")).not.toThrow();
});
it("doesn't render the default case if there is a matching case provided", () => {
render(
<Switch expression={true}>
<Case value={true}>Truthy Case</Case>
<Default>Default Case</Default>
</Switch>
);
expect(() => screen.getByText("Truthy Case")).not.toThrow();
expect(() => screen.getByText("Default Case")).toThrow();
});
});