Hopp til hovedinnhold

Teknologi / 4 minutter /

Slaying a UI antipattern with TypeScript and React

Fetch data like a knight in functional armor using a powerful Elm pattern.

Maleri fra 1800-tallet i svarthvitt av en soldat på et flyvende vesen som stikker en drage med spydet sitt.

Ruggiero Rescuing Angelica, an illustration for Orlando Furioso by Gustave Doré (Public Domain)

Ruggiero Rescuing Angelica, an illustration for Orlando Furioso by Gustave Doré (Public Domain)

Kris Jenkins made a great library in Elm called RemoteData. It makes data fetching more predictable and maintainable.
RemoteData is a nice data structure for external data. It returns one of of the following values, here shown in the Elm version:

With data like this, a developer is forced to handle the view for every case.

No more postponing the loading and error views for "later".

Here is an example of a view function in Elm returning something for every case. Ignoring any of them will prevent the app from compiling:

Like many of us, you might be stuck with React and TypeScript at best in certain projects. Luckily, this pattern is just as easy to implement with TypeScript.

In this first tutorial about RemoteData, we are building it with no direct dependencies other than TypeScript and React.

Throughout this series, we will make a TypeScript equivalent of this minimalistic Elm blog fetcher.

The antipattern

Kris Jenkins wrote a great article explaining this antipattern of not handling views for every state of data fetching.

Very shortly outlined from the article, this is the antipattern we want to handle:

Begin with the data structure

When the data is loading, the empty list of blogPosts shouldn't even be a possible state to access. Impossible states should actually be impossible.

Free standing loading states could work fine in a very small app. As complexity grows, it will become a growing pain point.

It will be just another switch to remember to turn on and off at the right time.

Let's do a super simple example.

Make a new React app with TypeScript and clear out the the entry file, which might be named index.tsx.

We already know the general data structure of this app. It's just displaying a list of blog posts.

We start with creating a Post type in addition to the RemoteData one.

Make it look like this:

The interesting part is of course the RemoteData type. It should be returned from a fetch function and this whole value should go directly into the state.

The E (error) and D (data) types are generic types that will be specified as we implement it.

Fetch data

Then create a function for fetching the data.

Throwing errors loose into the world is no good. It's better to catch them and return the data in an orderly fashion.

Pay attention to the return type in the function above.

The generic E and D types are now defined with specific types.

The State

To avoid introducing too many conepts in this article, I'll just use the useState hook for setting the posts state. It's of course considered forbidden magic as it's not immutable.

The default value is the initial NOT_ASKED value.

The getPosts function should instantly set a loading state when initializing the data fetching. The fetchPosts function will then decide the final state in the RemoteData lifecycle.

The view

Now to the final part: To be able to show the blog posts in the view, you make a case for every possible outcome of the RemoteData type.

The main function will look like this:

In cases where data is fetched automatically on page load, you can return the same view on both LOADING and NOT_ASKED.

Next up

In the next post, we are laying out the rendering of the RemoteData view a bit more elegantly than the switch statement.

Here is a teaser:

If you like this pattern, make it your own. Or if you are a regular JavaScript user, there are several resources showing you how to solve it without TypeScript. Here are some examples:

Any comments? Drop me a tweet or a comment.