- Published on
Avoiding large ternary operator statements in React
- Authors
- Name
- Danilo Gačević
- @danilothedev
Introducing the problem
Often when writing React applications, you are conditionally rendering some piece of UI based on some state. This condition can be as simple as a binary boolean condition, or it can be a more complex ternary operator with multiple nested statements.
Let's imagine that we are calling an API using react-query and that we are displaying different UI depending on the API call status.
const Example = () => {
const { data, status } = useQuery('users', fetchUsers)
return (
<Page>
{status === 'loading' ? (
<LoadingView />
) : status === 'error' ? (
<ErrorView />
) : (
<SuccessView data={data} />
)}
</Page>
)
}
These kinds of conditions can get hard to read sometimes.
Leveraging compound components and React.Children.map
function, we can write a cleaner solution by moving condition logic into a wrapper component.
const Example = () => {
const { data, status } = useQuery('users', fetchUsers)
return (
<Page.Wrapper status={status}>
<Page.Loading>
<LoadingView />
</Page.Loading>
<Page.Error>
<ErrorView />
</Page.Error>
<Page.Success>
<SuccessView data={data} />
</Page.Success>
</Page.Wrapper>
)
}
Page component implementation
const PageWrapper = ({ children, status }) => {
const renderChildren = useCallback(() => {
return React.Children.map(children, (child) => {
if (child?.type === Page.Loading && status === 'loading') {
return child
}
if (child?.type === Page.Success && status === 'success') {
return child
}
if (child?.type === Page.Error && status === 'error') {
return child
}
return null
})
}, [children, status])
return <>{renderChildren()}</>
}
export const Page = {
Wrapper: PageWrapper,
Loading: ({ children }) => children,
Success: ({ children }) => children,
Error: ({ children }) => children,
}