Separating UI states from API Request Status
Obvious status quo that we never challenged
function MyComponent() {
const {
isLoading,
error,
hasFetched,
isError,
data,
} = useApi(...)
if (!isLoading && hasFetched && isError) {
return <Error error={error} />
}
if (isLoading) {
return <Spinner />
}
if (hasFetched && !isError) {
return <RealUi />
}
}
Lessons from Portal (or pretty much any other project anywhere else)
- No way to ensure we make these checks everywhere.
- Entropy and convolution
- Facing the same problem for years and not once taking a step back and solving architecturally once and for all. (Skeletons, Error UIs, spinners, whatever else)
function MyComponent() {
const {
isLoading,
error,
hasFetched,
isError,
data,
} = useApi(...)
if (!isLoading && hasFetched && isError) {
return <Error error={error} />
}
if (isLoading) {
return <Spinner />
}
if (hasFetched && !isError) {
return (
<div>
{data.items && data.items.length && <div>{data.items.map(item => item.name)}</div>}
{ /* after some runtime error in prod we add the next line */}
{!data.items || !data.items.length && <div>No results found</div>}
</div>
)
}
the way forward
"But my app has different Error and Skeletons for each little UI block"
type ReactLeft<ComponentProps = {
title?: string
reason?: string
}> = { type: UI_STATES, props?: ComponentProps };
const error: ReactLeft<{ title: string }> = {
type: UI_STATES.error,
props: {
title: 'No privileges',
reason: 'You are trying to access "creditCards_db" without read scopes.'
}
}
const loading: ReactLeft = {
type: UI_STATES.loading,
}
const invalid: ReactLeft<WildAssProps> = {
type: UI_STATES.invalid,
props: {
some: 'random',
prop: 'types',
thatYour: 'component',
needs: '!!!',
}
}
import { RandomFactsAboutPotatoes, CuteBrokenPotatoGraphic } from './LeftComponents'
function Aloo() {
const data: either.Either<ReactLeft, Potato[]> = useApi(...)
return (
<div className={layoutWrapper}>
{
pipe(
data,
either.fold(
reactLeftToJSX({
[UI_STATES.loading]: RandomFactsAboutPotatoes,
[UI_STATES.error]: CuteBrokenPotatoGraphic,
}),
(user) => (
<PotatoList />
),
),
)
}
</div>
)
}
La fin
ReactLeft
By Aviral Kulshreshtha
ReactLeft
- 15