withSuspense
It's a higher-order component that wraps a given component with React's
Suspense component, providing a fallback UI to display while the main component
is loading. It uses forwardRef
to pass refs to the wrapped component and
handles the fallback logic to decide what to display while waiting.
Within the returned function, the code checks if FallbackComponent is a function (indicating it is a React component). If it is, it renders FallbackComponent with the same props and ref; otherwise, it uses FallbackComponent directly as the fallback content.
import { ComponentType, Ref, Suspense, forwardRef } from "react";
export function withSuspense<P extends object>(
Component: ComponentType<P>,
FallbackComponent: ComponentType<P> | ReactNode,
) {
return forwardRef((props: P, ref: Ref<HTMLElement>) => (
<Suspense
fallback={
typeof FallbackComponent === "function" ? (
<FallbackComponent {...props} ref={ref} />
) : (
FallbackComponent
)
}
>
<Component {...props} ref={ref} />
</Suspense>
));
}
Example Usage
Fallback as function component
type UserCardProps = {
userId: string;
};
function UserCard({ userId }: UserCardProps) {
const user = useSuspenseQuery({ queryKey: [userId], queryFn: fetchUser });
return (
<div>
<img height={200} src={user.image} />
<h3>{user.fullname}</h3>
</div>
);
}
function Fallback({ userId }: UserCardProps) {
return (
<div>
<Skeleton height={200} />
<span>Loading user {userId}</span>
</div>
);
}
export default withSuspense(UserCard, Fallback);
Fallback as plain ReactNode
type UserCardProps = {
userId: string;
};
export const UserCard = withSuspense(
({ userId }) => {
const user = useSuspenseQuery({ queryKey: [userId], queryFn: fetchUser });
return (
<div>
<img height={200} src={user.image} />
<h3>{user.fullname}</h3>
</div>
);
},
<div>
<Skeleton height={200} />
</div>,
);