React Tip: Making Use of switch-case in JSX
When you need to conditionally render a component based on multiple conditions
Situation
Imagine that you want to render different components based on the fetchStatus
prop. Its possible values are loading
, success
, and error
.
function BlogContent({
fetchStatus,
data,
}: {
fetchStatus: 'loading' | 'success' | 'error'
data: string | null
}) {
// Conditional rendering here
// ...
}
First Attempt
Nested ternary! I see this approach being used in too many codebases. I think this approach should be avoided when there are more than 2 possible rendering branches, as it's not good for readability in my opinion.
function BlogContent({
fetchStatus,
data,
}: {
fetchStatus: 'loading' | 'success' | 'error'
data: string | null
}) {
// Nested ternary. This should be avoided.
return (
<div>
{fetchStatus === 'loading' ? (
<span>Loading...</span>
) : fetchStatus === 'success' ? (
<p>{data}</p>
) : (
<span>There was an error</span>
)}
</div>
)
}
A Better Approach: Extract the Condition from JSX
A better approach is to extract the condition from JSX and store the result in a variable, e.g. content
in this case.
function BlogContent({
fetchStatus,
data,
}: {
fetchStatus: 'loading' | 'success' | 'error'
data: string | null
}) {
// Extract the condition from JSX and store the result in "content"
let content
if (fetchStatus === 'loading') {
content = <span>Loading...</span>
} else if (fetchStatus === 'success') {
content = <p>{data}</p>
} else {
content = <span>There was an error</span>
}
// Use "content" variable in JSX
return <div>{content}</div>
}
This approach is good enough. However, what if I want to put the full if-else condition right inside the JSX? Since it allows me to easily scan the code from top to bottom without having to jump around the lines.
Making Use of switch-case Inside JSX
Because we can only put an expression inside JSX, we need a little trick to overcome this limitation: IIFE to the rescue!
function BlogContent({
fetchStatus,
data,
}: {
fetchStatus: 'loading' | 'success' | 'error'
data: string | null
}) {
// Using IIFE to put the condition inside JSX
return (
<div>
{(() => {
switch (fetchStatus) {
case 'loading':
return <span>Loading...</span>
case 'success':
return <p>{data}</p>
case 'error':
return <span>There was an error</span>
default:
throw new Error('There is an unhandled case')
}
})()}
</div>
)
}
That's a little tip for today. Happy coding everyone!