The loading anti-pattern.
Tags: javascript, react, reactnative
Published: 2020-11-17 07:07:34

The loading anti pattern is something that I have experience when I need to fetch a data and then display it.

Normally when you want to display a data from an API, there are 5 condition that you want to fulfill.
1. Show initial data. It could be a blank screen.
2. Show loading indicator.
3. Show the result.
4. Show different message if the result is empty.
5. Show an error if there is one.

So let's try to built this.

First the data structure. Most likely it would look like this
```javascript
const data = {
  items: [],
  isLoading: false,
}
```
The `items` is a list I want to display and `isLoading` is a boolean so that I know whether it is loading or not.

So lets try to display either the loading or the list component first.

```jsx
<List>
 {isLoading ? <Loading/> : <Items items={items} />}
</List>
```
So far so good. Now we need to differentiate between the result that have a list and a result that return an empty list. Normally I would do it like this.

```jsx
<Items>
  {items.length > 0 ? items.map(item => <Item>{item.name}</Item>) : <Typography>List is empty</Typography}
<Items/>
```

Notice that I use `items` as an indicator on whether the result from an API is empty or not. 

This can be a problem because it will show `List is empty` initially even when we haven't fetch the data yet.

One way to solve this is to just set `isLoading` to true on the initial data

```jsx
const data = {
  items: [],
  isLoading: true,
}
```

Now let's try to handle a case where an API returns an error. First we need to add extra value to the data.

```jsx
const data = {
  items: [],
  isLoading: true,
  isFailure: false,
}
```
Now I can use `isFailure` as an indicator to show the error message.

```jsx
<Box>
 {!isFailure ? <List/> : <Error/>}
</Box>
```

Putting everything together, you have something that looks like this
```jsx
const data = {
  items: [],
  isLoading: true,
  isFailure: false,
}

const Page = ({data}) => 
(
<Box>
 {!data.isFailure ? <List data={data} /> : <Error/>}
</Box>
)

const List = ({data}) => 
(
<Box>
 {data.isLoading ? <Loading/> : <Items items={data.items} />}
</Box>
)

const Items = ({items}) => 
(
<Box>
  {items.length > 0 ? items.map(item => <Item>{item.name}</Item>) : <Typography>List is empty</Typography}
<Box/>
)
```


So now that I handle all the condition. You might be wondering what is the problem? 

Well, the problem is that I'm trying to describe the state of the data using 3 different value. `items`, `isLoading` and is `isFailure` and this makes your render logic more complex than it should be.

I have nested `if` to cater between different state of the data.
```javascript
 !isFailure ? isLoading ? items.length > 0
```

There can also be an invalid state where both `isLoading` and `isFailure` can be `true`.

The problem lies with trying to use boolean to describe the state of the data. Boolean can only represent 2 state of the data but we know now that the data can have 4 state. Initial, loading, failure and success. This is why you end up with so many value.

So how do we fix this? 

I was looking at a video about Elm and one of the talk is about this anti pattern and how to solve them. You can view it [here](https://www.youtube.com/watch?v=NLcRzOyrH08).

Basically, what you should do is to have a single value to represent all the possible state of your data. He suggested the state to be `notAsked`, `loading`, `failure` and `success`.

So now you can describe your data like this

```jsx
const notAsked = 'notAsked'
const loading = 'loading'
const failure = 'failure'
const success = 'success'

const data = {
  items: [],
  state: notAsked,
}
``` 

This pattern solve a couple of problems.
1. They can no longer be an invalid state.
2. There is a single value to represent the state.

This can make your render logic a lot more simple.

```jsx
switch (data.state) {
 case notAsked:
  <Inital/>
  break
 case loading:
  <Loading/>
  break
 case success:
  <List/>
  break
 case failure:
  <Error/>
  break
}
```

If you don't want to watch a video, you can also read his post [How Elm slays a UI antipattern](http://blog.jenkster.com/2016/06/how-elm-slays-a-ui-antipattern.html). 

Although it is geared towards Elm but I believe it can be implemented elsewhere.