🎣 Introducing React Data Fetching

Charles D. Mangwa
10 min readMar 15, 2018

--

Let’s fetch some data! Credits — Sally Dixon

I had the opportunity to present this library during a lightning talk at react-europe 2018 where I talked about declarative data fetching in React!

As a React Native developer, I always had to deal with HTTP requests. That’s probably the core element of any app: being able to exchange data between you and your end user. Probably like many of you, I started by using good old plain functions that I’d copy/paste here and there, and just use them over and over. As you can guess: it wasn’t that sexy…

After a bit of refactoring, I came up with a helper function that was meant to get me a better, cleaner, faster, prettier, stronger & shinier code, and I thought I was done. How foolish of me… 🤦‍

For the past few months, I’ve been wrapping my head around a new React pattern: Just Components. I’ve already mentioned this is in a previous post about a new React Native navigation library, but this declarative approach really struck a chord with me. With that philosophy in mind, I’ve spent around 10 months designing, using, refactoring (and today publishing): 🎣 React Data Fetching!

🎉 The most convenient way to <Fetch> data in your React app! 🎉 (imho)

Philosophy 🤔

If you have been following the news about React 16.3.0 new context API, or are familiar with React Router or Downshift for instance, you should be familiar with the concept of render props.

Long story short: React Data Fetching allows you to interact with your REST API using a single React component.

Wait…Fetching data in render? 😳

Let me explain: it’s not what you think it is! I admit it, during the first iterations of the library I was naively doing what’s widely considered as an anti-pattern: fetching my data and calling setState in my render method. Turns out it wasn’t the most brilliant idea I ever had…

Dan Abramov took a little bit of his time to explain where my mistake was, and gave me the hint I needed to bring all the pieces together:

Issue #1116

Thus, if you don’t explicitly pass a children to <Fetch>, it will always render null and do its business with the other props you provided, as we’ll see below.

Furthermore, as seen in Dan Abramov’s talk at JSConf Iceland 2018, sounds like fetching data in render will be a thing in a future React release this year, thanks to the Suspense feature (starts at 13:20):

So for now, sit back, relax, get yourself a good cup of kombucha and follow along. Let’s dive into RDF!

Simple use case 👶

Why not start with a pretty straightforward example, to introduce you to the API nice and slow? The most simple way to use RDF would be to just fetch data & use it directly. Nothing to crazy here, so here is how you can achieve that:

If we try to break this piece of code down, we see that the library will need at least a url. As you can see, I didn’t mention any HTTP method here, so RDF will use 'GET' by default. But if you happen to need it, it’ll be as easy as adding this prop: method="POST".

From there, if my API responds successfully, I’ll have a data field available in the result object. Here I used the Function as Child Component (FaCC) pattern to exploit data.

I found this overall approach incredibly convenient and to be honest I’m still wondering how I could live without it! But let’s move onto something more interesting.

Slightly more complex use case 🤯

When we deal with API calls, things aren’t usually as seamless as our previous example. Generally, we’ll have to take care of loading states, errors handling, data saving, etc. Turns out you can actually do all that with React Data Fetching:

There is a lot going on in this snippet, isn’t it? React Data Fetching allows you to pass a bunch of useful props here: method,body and params. body is made for the methods that will need it, and params is used to construct the URL of your 'GET' call. Both of them only accept an object as a value, while method accepts these strings for now:

type Method =
| 'DELETE'
| 'FORM_DATA'
| 'GET'
| 'HEAD'
| 'PATCH'
| 'POST'
| 'PUT'
| 'TRACE'

'FORM_DATA' used for binary files upload has a special treatment under the hood because it uses a 'POST' method but with a multipart/form-data content type, not application/json. To keep this simple, the exposed API is still the same: you’ll just need your body and method="FORM_DATA".

Fetchcycle hooks ⚓️

Here is where things get more exciting! RDF give you access to 3 useful props when it comes to deal with your data state: onLoad, onFetch & onError.

That’s a bunch of new props there! We have onLoad for instance, called when the library starts fetching your data.

But usually you’ll use this to display/hide your loader. So guess what? React Data Fetching provides a prop for that too! You can directly use the loader prop and pass a function/component here too. The library will automatically render your loader while loading data, and hide it by itself when your data is fetched.

Next on the list: onFetch/render combo. The typical use case here would be to use onFetch to call setState(cf #L28) for instance, while using render to… render your components (cf #L29 & #L10-#L14).

Note: You cannot use onFetch to render something nor use render to call setState.

By the way, you’ve noticed that React Data Fetching supports both methods to render your function as a child: a render prop or just React’s children inside the component.

Finally, there is a third way to render something with React Data Fetching. If you want to render a component without using an inline function, you can use the component prop.

This lets you pass a component like so component={ProfilePage}, and the library will just have to call React.createElement and pass the result inside ProfilePage's props.

Note: Don’t use inline functions inside component as React.createElement will create a new element every render. Prefer children or render for inline rendering.

All these props are here to facilitate the use of the library, so that you can easily let the user know what’s going on when you fetch data.

Extra goodies 🎁

I want to talk a bit more about a few (nice) things that are implemented directly in the library to improve your DX.

Universality 🌍

React Data Fetching only needs React to work.

This means that you can use is it seamlessly inside any React & React Native app!

You probably noticed that my previous snippets are quite the same, even if they are from Preact (App.js), React Native (Container.js & News.js) and React (Timeline.js) apps.

Timeout ⏱️

Whenever we write our requests, as well as we may handle results, errors, etc. everything boils down to your user network connection. So what if your end user is on a slow network? We don’t want your call to stay pending forever!

That’s why <Fetch> implements a timeout prop (value in milliseconds) you can use as a fallback when your request exceeds this limit. To catch this event, you’ll just have to use onTimeout prop, to display something to your user for instance.

Note: Any request that exceeds your timeout will automatically be aborted before calling onTimeout.

Progress ♻️

You’re a good developer. You’re using 'FORM_DATA' method to upload your user’s profile picture and because UX is really important to you, you want to display the actual progress, not just a simple spinner. But how could you do that? How about another prop!

onProgress is a prop you can call whenever you’re making a request — regardless of the method, to handle its progression. The inline function you’ll pass will receive a single argument of the following shape:

type Progress = {
bubbles: boolean,
cancelable: boolean,
lengthComputable: boolean,
loaded: number,
target: EventTarget,
total: number,
type: string
}

Refetching 🔄

When we talk about applications in general, we usually have this very common pattern where our list of articles, photos, comments, etc. will need to load more data at some point. How could RDF deal with this use case? Through a prop obviously! You’ll just need to pass a refetch prop and change its value to update your call. This follows the same idea as FlatList‘s extraData prop in React Native.

Here is a small example of how you could use React Data Fetching to get the 20 last posts from our Nyan API, and load 30 more when the user gets to the bottom of the list:

That’s pretty cool isn’t it? 🤩

By the way, you’ll notice the resultOnly prop (#L32) which allows you to directly get access to the result, and not having to get it through an object!

Returned data 📥

You may be wondering: “What’s that object from which you extract data or error every single time? 🤔”. Whenever you use component, onError, onFetch, render or a child function, React Data Fetching will return an object of this shape inside of it:

type ReturnedData = {  
data?: Object,
error?: Object,
isOK?: boolean,
request?: XMLHttpRequest,
status?: number,
store?: Object
}

Obviously, the available fields will depend on the returned data. For instance, if you get an error, you’ll have access to error but not to data and vice versa.

One more thing… 😏

Something in my previous piece of code may have caught your attention: store?: Object.

What if I told you React Data Fetching lets you fetch data directly from your Redux store?

Well, it does actually! You’ll just need to implement <ConnectedFetch> at the root of your app, right under your <Provider> from react-redux. Implementing <ConnectedFetch> allows you to do that and even more:

Now you can write way less code in several use cases, but still have access to more informations at the same time:

There are a bunch of nice & useful things enabled by the use of <ConnectedFetch>. It allows us to remove every occurence of headers prop and automatically share it among all our instances of <Fetch>. Same goes for loader (you can automatically render the same loader in any <Fetch>) & timeout (same value for all your calls).

You’ll also noticed that url could become path from now on, because <ConnectedFetch> here again will broadcast your api URL to every <Fetch> inside your app.

Note: You cannot use path without implementing <ConnectedFetch api="...">, you’ll have to use url instead.

However, using <ConnectedFetch> inside <Provider> isn’t mandatory! But if you do so, as I showed in my Root.js snippet, you’ll automatically access your Redux store inside the result object. But you could also manually pass a value: <ConnectedFetch store={myOtherStore}>.

This whole thing allows you to exploit data inside your store when needed, without having to write some extra stuff.

⚠️ Disclaimer: getting your Redux store is an experimental feature which might disappear in the future. ⚠️

It may also be completely rewritten due to the upcoming React v16.3.0 and its new context API.

But given that we have it for now, React Data Fetching has a little extra something for those of you who will need to fetch data only from their (Redux) store:

By writing path="store" (not path="/store" which could be an API route), you’re basically telling RDF:

“Can you send me back the content of my store please? Oh by the way, you’ve probably noticed but: no need to contact the API, this is not a network call. Thanks!”

And with the exact same syntax as usual, you get access to your store inside data this time (because it’s the result of your “call” 😉). Hope you’ll like it!

But how about no? 🙅

If you happen to prefer good old functions to write your calls, React Data Fetching also got you covered!

requestToApi type looks like this:

requestToApi = ({
body?: Object,
headers?: Object,
method: Method,
onProgress?: Function,
onTimeout?: Function,
params?: Object,
url: stirng,
timeout: number
}): Promise<any>

Pretty straightforward here again. You may be wondering why this function requires an object instead of just passing parameters? The main reason is that I’ve been heavily inspired by ReasonML’s labeled arguments.

This syntax allow us to fake this behavior in JavaScript, and just write for instance:

requestToApi = ({
path: 'https://api.nyan.com/openChat',
method: 'GET',
params: { start: O, limit: 100 }
})

instead of:

requestToApi = (
_,
_,
'GET',
_,
_,
{ start: O, limit: 100 },
'https://api.nyan.com/openChat',
_,
)

where you have to write useless parameters, and don’t necessarily know what each one of them represent, can’t write them in the other you’d like, etc.

But to be honest with you, this was also an opportunity for me to do my daily ReasonML shameless plug 😅…

This library is the fruit of several months of prototyping, testing & refactoring and I’m thrilled to be able to put it out there today! My job with it is not done and I still have a ton of features I’d like to add in mind (just check out the #todo section in the README if you’re curious!).

I’m totally opened to any feedbacks, ameliorations, critics, PRs you may have. Just chime in on the repo or ping me @Charles_Mangwa. Until then, simply run yarn add react-data-fetching and enjoy API calls again!

--

--