React-refetch

Now, our HoC works as expected and we can reuse it across the codebase without any problems. The question is, what should we do if we need more features?

For example, we may want to post some data to the server or fetch the data again when the props change. Also, we may not want to load the data on componentDidMount but apply some lazy loading patterns instead.

We could write all the features we need, but there is an existing library that has a lot of useful functionalities, and it is ready to be used.

The library is called react-refetch, and it is maintained by developers from Heroku. Let's see how we can use it effectively to replace our HoC.

From the previous section, we have a List component, which is a stateless functional component that can receive a collection of gists; it displays the description for each one of them:

 import React from 'react';
import { array } from 'prop-types';

const List = ({ data: gists }) => (
<ul>
{gists.map(gist => (
<li key={gist.id}>{gist.description}</li>
))}
</ul>
);

List.propTypes = {
data: array
};

By wrapping this component inside the withData HoC, we can provide data to it in a transparent way through props. We can enhance it by passing the URL of the endpoint.

With react-refetch, we can do the same thing. First of all, we need to install the library:

  npm install react-refetch --save

Then, we import the connect function inside our module:

  import { connect } from 'react-refetch';

Finally, we decorate our component using the connect HoC. We use the partial application technique to specialize the function and reuse it:

  const connectWithGists = connect(({ username }) => ({ 
gists: `https://api.github.com/users/${username}/gists`
}));

The preceding code needs a bit of an explanation.

We use the connect function, passing a function to it as a parameter. The parameter function receives the props (and the context) as parameters so that we can create dynamic URLs based on the current properties of the component.

Our callback function returns an object where the keys are the identifiers of the request, and the values are the URLs. The URL is not limited to being a string; we'll see how we can add multiple parameters to the request later.

At this point, we enhance our component with the function we just created, as follows:

  const ListWithGists = connectWithGists(List);

We now have to make small changes to the initial component to make it work well with the new HoC.

First of all, the parameter is not called data anymore; it is called gists. React-refetch will inject a property with the same name of the key that we specified in the connect function.

The gists prop is not the actual data—it is a particular type of object, called PromiseState.

A PromiseState object is a synchronous representation of a Promise, and it has some useful attributes such as pending or fulfilled that we can use to show a spinner or a list of data.

There is also a rejected property that we can use in case of errors.

When the request is fulfilled, we can access the data we wanted to load using the value property and loop through it to display the gists:

  const List = ({ gists }) => ( 
gists.fulfilled && (
<ul>
{gists.value.map(gist => (
<li key={gist.id}>{gist.description}</li>
))}
</ul>
)
);

As soon as the stateless functional component is rendered, we can check to validate if the request is fulfilled; if it is, we show the list using the gists.value property.

Everything else remains the same.

We also have to update the propTypes and change the name of the received prop and its type:

import { object } from 'prop-types';
...
List.propTypes = {
gists: object
};

Now that we have this library in our project, we can add more functionalities to our List component.

For example, we can add a button to start the gists.

Let's start with the UI and then add the real API call, thanks to react-refetch.

We do not want to add too many responsibilities to the List component as its role is to display the list; so, we change it to use a subcomponent for each row.

We call the new component Gist, and we are going to use it inside the loop in the following way:

  const List = ({ gists }) => ( 
gists.fulfilled && (
<ul>
{gists.value.map(gist => (
<Gist key={gist.id} {...gist} />
))}
</ul>
)
);

We just replaced the <li> element with the Gist component, and we spread the gist object to it so that it receives single properties and becomes easier to test and maintain.

The Gist component is a stateless functional component because the starring logic is handled by react-refetch and we do not need any state or event handlers.

The component receives the description and, for now, the only difference from the previous markup is that it has a +1 button, to which we will add some functionalities soon:

  const Gist = ({ description }) => ( 
<li>
{description}
<button>+1</button>
</li>
);

Gist.propTypes = {
description: string
};

The URL of the endpoint to star a gist is as follows:
https://api.github.com/gists/:id/star?access_token=:access_token.

Here, :id is the ID of the gist that we want to star, and the access token is the authentication token that's required to run the action. There are different ways of getting an access token, and they are well explained in the GitHub documentation.

They are also outside the scope of this book, so we are not going to cover them in this section. The next step is to add a function to the onClick event of the button to make an API call with the ID of the gist.

The connect function of react-refetch accepts a function as the first argument, and the function has to return an object of requests, as we have previously seen. If the values of the requests are strings, then the data is fetched as soon as the props are available.

If the value of a request key is a function instead, it gets passed into the component, and it can be fired lazily. For example, it can be triggered when a particular event occurs.

Let's delve into the code:

  const token = 'access_token=123';

const connectWithStar = connect(({ id }) => ({
star: () => ({
starResponse: {
url: `https://api.github.com/gists/${id}/star?${token}`,
method: 'PUT'
}
})
}));

First, we partially apply the collection function, and we use the id prop to compose the URL.

We then define an object of requests, where the key is star and the value is a function that, again, returns an object of requests. In this case, the value of the starResponse key is not a simple string, but an object with two parameters: URL and method.

This is because, by default, the library fires an HTTP GET, but in this casewe are required to use a PUT to star a gist.

It is now time to enhance our component:

  const GistWithStar = connectWithStar(Gist);

Also, it is time to use the star function inside it to fire the request:

  const Gist = ({ description, star }) => ( 
<li>
{description}
<button onClick={star}>+1</button>
</li>
);

Gist.propTypes = {
description: string,
star: func
};

As you can see, it is very simple; the component receives the star function, where the star is the name of the request that we defined in the connect function. The function gets fired when the button is clicked on.

This is the final result in the browser window:

You may have noted that thanks to react-refetch, we can keep our components stateless and unaware of the actions that they are firing.

This makes tests easier, and means that we can also change the implementation of the HoC without modifying the subcomponent.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.93.136