How To Use an API with ReactJS
A client recently asked me, “Do you know how to use APIs with ReactJS?”.
Frankly, I hesitated when answering because I thought,
There wasn’t enough context for me to figure out what kind of Application Programming Interface (API) she was talking about, and you can learn more about the various types of APIs here.
Consider this, my favorite definition from the article linked above concerning different types of APIs is;
…an API provides interactions between one software and another, but not users.
Given a broad definition, asking a developer if they know how to use APIs is like asking a chef if they know to use the stove.
On rare occasions, they may not.
However, truthfully it depends on the type of stove and what they are using it for.
If you grant me the comparison: using an API depends on the API and what I am using it for.
Before diving into how to use API’s with RactJS, let us first go over some basic definitions:
RapidAPI is a platform for accessing web-based APIs.
The most popular type of web API is a Representational state transfer API or RESTful API for short. To be brief, it follows an architecture that uses predefined and stateless operations to access web resources.
Using web APIs requires the use of HTTP requests. Different types of requests include GET, POST, DELETE, PATCH, etc. If you have written some code and it needs to communicate with code somewhere else, it will send an HTTP request over the internet. Next, let’s discuss the HTTP request types just mentioned. You will notice that the types are matched to verbs that are similar to their function:
- GET: Used to request data from an endpoint
- POST: Sends data to an endpoint
- DELETE: Remove data from an endpoint.
- PATCH: Update a record or data value at an endpoint.
These types of interactions are external from the local environment, and therefore, the APIs that are providing the data or services are called external APIs. It is difficult to fathom how useful it is to have the capability to access external APIs for various data and services.
As previously mentioned, external APIs offer data, services, or both. Having access to the API can be valuable, therefore, you may need to buy a subscription to use it. Don’t worry, there are still plenty of free APIs.
Furthermore, the hypothetical external API does not have unlimited resources, so letting software call the API an unlimited amount of times at no cost could render it useless. Consequently, API providers will issue secret API-keys to monitor usage over time. Using RapidAPI allows the developer to only deal with one key (RapidAPI’s key), otherwise, developers would need to have API-keys for all of their APIs. Most of the time API-keys need to be kept secret, however, some services will issue public API keys.
Soon we will learn how to use the RapidAPI dashboard to simplify our understanding of how these parts work together, but first, let’s talk about React!
What is React?
The ReactJS library is useful for handling the relationship between views, state, and changes in state.
Views are what the user sees rendered in the browser. State refers to the data stored by different views that typically rely on who the user is, or what the user is doing. For example, when you sign into a website it may show your user profile (view) with your name (state).
The state data may change user-to-user but the view remains the same. Expanding on our example, let’s suppose the website is an e-commerce site. If you are signed in, the website may have a Products You Might Like section. However, if you are not signed in, rendering that view may not make sense. ReactJS helps developers manage that kind of scenario with ease.
The sections below will be a brief introduction to ReactJS. For a more comprehensive tutorial, you can visit the ReactJS.org tutorial page.
Views are created inside of components. There two types of components: class and function. Below is a class component:
Notice that we declare a state object in the constructor. This where we can store data to be used inside the view. Speaking of the view, it is rendered to the page inside of render() .
A function component may look like:
Unlike the class component, our function returns the view and does not need a render function. Also, the state is controlled by the React hook function useState .
Later on, we will use class components in the example app.
We can pass data or functions down into components through props. Here’s an example:
In the above example, the string ‘this is the starting value for the variable’ is rendered to the page in replace of
The child component is rendered inside of the parent component and can inherit data passed down to through properties.
This a confusing concept at first because it does not read procedurally, but it will make more sense when we start using actual data. It’s a big reason why React can be so powerful.
JSX looks like an HTML templating language but is more powerful. We already saw how JSX can embed data into a view using curly brackets during our previous example. It looks very similar to HTML but has many differences. A few are:
- passing custom props as attributes on an element tag
- Instead of assigning CSS classes with class= , JSX uses className= . Camel casing is used for event listeners as well (I.e onClick, onHover ).
- closing tags are different depending on the elements. Below are a few examples.
HTML input and image tags:
<input type=»text» required>
JSX input, and image tags:
<input type=»text» required />
In addition to the above subtle changes, JSX tags can be self-closing. A valid JSX element could be.
Side effects occur when a function goes outside of its scope or it is affected by something outside of the function body. Making an HTTP request and saving the result to the components state is a side effect.
In React, components have methods that are executed during the different phases of the components. These methods are called lifecycle methods.
It takes practice to figure out where and how certain lifecycle methods should be used. Regardless, the most important for HTTP requests is componentDidMount with class components and useEffect with functional components.
Grabbing the examples above, let’s add the methods we are talking about to the components.
These methods are used because they are called immediately after the component is mounted to the DOM (Document Object Model).
Here an example of how this works:
- User visits webpage
- The component is mounted to the DOM (webpage)
- The componentDidMount or useEffect method is called sending off the HTTP request
- The webpage gives an indication it is loading data
- The data is received from the external API and added to state (side effect)
- The component renders with the data that was fetched.
That was a bit of a crash course, and I do not expect you to have a full understanding of React. However, in this circumstance, the best way to learn is to build it! Therefore, let’s get started!
Let’s make sure that you are fully prepared to advance and build out API calls using ReactJS. Below are a few prerequisites:
. It’s free to sign-up and the API we use in the example below is free as well!
- An internet connection and a browser that isn’t Internet Explorer. IE has some compatibility issues that can be easily avoided. I recommend Firefox or Google Chrome.
- A reputable text editor. I use Visual Studio Code, however, another popular example is Sublime Text.
- Knowledge of how to use the command-line on your operating system. The commands are simple and should be similar across systems, however, there might be a slight variation. Looking up the appropriate command should be quick and easy.
- NodeJS and NPM installed locally. There are installation instructions on NodeJS.org.
NPM stands for Node Package Manager. Unsurprisingly, it is the package manager for ReactJS and NodeJS packages. We will use it to download React and other dependencies.
How to Fetch/Call an API with React
In this example, we will learn how to send HTTP requests to a database to get, create, update and delete external data using an API on RapidAPI with React. Then, we will learn how to display this data on the webpage. Finally, I will point you towards more advanced examples so you can continue to grow!
1. Create a Basic Project Structure
Make a new folder. I named mine react-api-call .
Open up your text editor inside of the new folder and navigate into the new folder with your terminal.
Create the following folders:
Inside public create the file index.html and add the following code to it.
There is not much going on in this HTML file. We created a container for the app with an id of ‘App’ and loaded in:
- React component (not created yet)
Next, inside of the public folder, create the folder js . This will be an empty folder.
After that, inside of src folder create the empty file app.js . Your project structure should look like
The above picture does not include the node_modules folder that should be in the project root as well.
2. Add React Component
Back in the terminal run these two commands:
- npm init -y : Creates an npm package in our project root
- npm install babel-cli@6 babel-preset-react-app@3 : Installs the packages we need to convert JSX to HTML
In src/app.js add the following code:
Notice we have inserted empty functions to fill with APIs calls and bound them to the component in the constructor function. Also, we initialized our state and defined variables that we need to keep track of.
Taking a look at the JSX, you notice that we are using camel-case (camelCase) to define attributes and properties. Furthermore, we are controlling the inputs by assigning them to values in state and passing an onChange handler.
Next, we need to set up a live server to make the development process easier to understand.
If you are using VSCode you can download the plugin Live Server. This allows you to watch your changes update automatically.
If you are not using VSCode there is most likely an easy way to get the same functionality with your text editor with the help of your favorite search engine.
After installing the plugin in VSCode, select the ‘Go Live’ button which will start the application on a port locally (i.e http://localhost:5050). The browser gives you the option to select the directory to run the server in choose public .
Although the site is live, nothing will display because we are not compiling our React code in the src directory.
In the terminal run:
npx babel —watch src —out-dir public/js —presets react-app/prod
The command will automatically compile the code in src and place it into the appropriately named folder in public/js .
The output in your terminal should be:
Now, you should see our React file code appearing in the browser.
3. Add API Calls
Our interaction with an API depends on that specific API. It may only offer certain types of HTTP methods, endpoints, and parameters. Whatever the case we need instructions on what that API expects our requests to look like. I have chosen to use the FaiRESTdb API for this example so I can demonstrate the four main types of API calls that we should know (GET, POST, PUT, DELETE).
Let’s explore the dashboard tool.
Search for the API on the marketplace or follow the link to the API dashboard. In the dashboard you can find:
- endpoint details
- community discussions
The different types of endpoints that are available can be found on the left side of the interactive dashboard.
Depending on which endpoint is selected, a definition and parameter information will be available in the center of the dashboard.
Finally, the right side of the dashboard builds code snippets based on the endpoint and the parameters that we enter. In this way, the dashboard works from left-to-right.
In addition to ready-to-use code snippets, the right side of the dashboard displays sample response objects that are important to planning our application. Most APIs allow us to test endpoints and receive real response data right in the API dashboard.
We can create a database for our sample application in the dashboard after subscribing because the tests are real requests.
Click the blue ‘Subscribe to Test’ button on the dashboard.
The pricing panel shows us the available plans and quota restrictions for the API. Notice, we get 500 calls a month, then it’s $0.01 for each subsequent call. You may think that would be difficult to keep track of, however, RapidAPI has a developer dashboard that allows you to track your API calls!
Select the Basic plan.
Now that we can use the API, head back to the dashboard and select the Create Database endpoint. Our example will need a database to communicate with and we can create one here in the dashboard.
Change the name value in the request to ‘friend’.
Hit the ‘Test Endpoint’ button. A response object should appear on the right side of the dashboard telling you that the database was created successfully!
Now that we have a database, we need to make a friend model. Instead of setting up another function, for the sake of simplicity, we will create a basic model with the dashboard.
Create a Model
Just like we did for the database, we need to select the endpoint, fill in the parameters, and send the request.
Select the Create Model endpoint and add the appropriate parameters.
Select the Test Endpoint button. The response should be similar to:
Great! Now it’s time to add the API code to our React component!
Grab Code Snippets
The process for retrieving the code snippets is as follows:
- Select the endpoint
- Change parameters and schema
- Select the desired language and library
- Copy the code
Let’s do this for the Add Entity (POST), Get All Entities (GET), Update Entity (PUT), and Delete Entity (DELETE) endpoints.
Remember, the database name is ‘friend’, the model name is ‘friendModel’, and the schema needs to match the friend model schema we set up.
I’ve edited the snippets a little bit so they can receive JSON responses and send JSON requests. Furthermore, I made React specific changes to show how we are going to getting the variables from the component state for the request.
Here’s what the snippets should be for each endpoint (make sure you are substituting your RapidAPI key):
Get All Entities:
IMPORTANT: Comment out the componentDidMount function after the code is added. This will stop the Get All Entities function from firing every time we make and edit and the app rerenders.
Next, add the snippets to the application inside the appropriate function. We want the app to retrieve all the entities when the component loads, therefore, the Get All Entities snippet goes in the componentDidMount function.
Add Snippets to Functions
For the snippets to work, I have added the necessary state variables that the functions need.
After adding the snippets to their corresponding functions, and modifying JSX, src/app.js should look like:
A few things to note:
- all the calls are from the same form, which can cause user experience issues, but for this example, it can work
- componentDidMount is commented out
Now, we can add, update, and delete friends from our application. Also, we can view the records that we are making using the RapidAPI dashboard to send requests and inspect the response.
How do we know that is work?
HTTP Status Codes
HTTP status codes provide information about a particular request. For example;
- 200 – the request has succeeded
- 201 – request successfully created something
- 400 – bad request (syntax, parameters)
- 401 – unauthorized
- 404 – page not found
- 500 – server error
Those are some of the most common. In our app, when we add check the status of requests in the developer tools.
In the Network tab, you can find the request that was just sent from the browser. Let’s try it out.
In the app add a friend to the database.
Open up the developer tools (I am using Google Chrome) and go to the Network tab.
Notice the request’s name was ‘friendModel’ and the status was 200! If a request fails, the browser may highlight the request red so it is easier to find.
We are golden in terms of sending HTTP requests with React. However, what if we want to view this data in our application? It would be much easier to make edits and see all of our friends!
How to Display API data in React
Previously, we added code to componentDidMount that retrieves the records in our database. However, we did not have a way to display that data on the page, so we commented it out.
Let’s create a new component that receives the response data as a prop, and displays that data when the page loads.
Above the App component in app.js add the component:
The above component is similar to App, however, it waits for this.props.friends to be defined before rendering the table rows. This is necessary because the parent component, App, needs to fetch the data. Therefore, the data will not be available when the component first mounts.
Next, add this component as a child of App and pass the property friends with the value this.state.friends .
Inside the JSX code of the App component, and underneath the closing </form> tag, add the following line.
Next, uncomment the componentDidMount function.
Finally, the friends that we have in the database will appear as rows in the table when the page loads!
This will make it easier to add, remove, and edit our friend list.
There are many ways that the form could be improved, for example, it could:
- display success and error messages
- clear the input after submitting
- only allow the user to submit fields that are used by the specific API call
However, this is one of the most basic ways to make a React component and I chose this method so we could focus on the important details of building components and making API calls. Not only that, I wanted you to understand React at a basic level before using an advanced project builder.
That being said, for a small project this could be fixed up to look—and behavior—nicer with little effort.
Despite instructing you to put your API-key in the front-end code for this example that is a bad idea, because it can be found if someone was looking for it. Therefore, whether you build a React app this way, or whether you use Create-React-App, if the React code is in the front-end it should not have an API-key.
This might leave you wondering, so what do I do? One example is to use a framework like Next.js that uses server-side rendering or a static site render like GatsbyJS. Unfortunately, even those solutions do not cover all scenarios so make sure you read up on what you are trying to do before you put your API key in a compromising position.
In the next section, I cover two examples of solutions for securing your app for API calls.
React API Call Examples
The above tutorial doesn’t use React but utilizes Netlify to make secure API requests without having to set up a backend server. Netlify offers serverless function capabilities that are not shipped with the frontend code, so API secrets can be added as environment variables later.
Reviewing the above tutorial would introduce you to an easy solution to securing third-party API requests.
If you are looking for more of a challenge the tutorial above is the answer. The frontend of the app is built with Create-React-App (CRA) which has been a popular choice for many when building larger React applications.
In addition to CRA, the example application sets up a backend NodeJS server that makes secure HTTP requests to a third-party API. This is another very common pattern in creating React applications and is why stacks like the M.E.R.N become popular (MongoDB, Express, React, Node).
Here’s another example using OpenWeatherMap:
And with Google Maps:
It’s difficult to build a React application without adding features that involve sending HTTP requests. There are many APIs that make our lives easier and it would be foolish not to utilize them! This introduction to React and API calls was brief so I hope you can sharper your skills with one of the tutorials I suggested above.
If you have any questions please leave a comment!
How to display API data using Axios with React?
Set-up the app, add Axios API call with API Key, and transform Axios Response data. Read more here: https://rapidapi.com/blog/axios-react-api-tutorial/
What is right way to do API call in React JS?
As best place and practice for external API calls is React Lifecycle method componentDidMount(), where after the execution of the API call you should update the local state to be triggered new render() method call, then the changes in the updated local state will be applied on the component view.
How to Consume REST APIs in React – a Beginner's Guide
React is a popular frontend library that developers use to create applications. And you will need to integrate APIs into your React application at some point if you want to build production-ready apps.
Every developer who wants to build modern, robust web applications with React must understand how to consume APIs to fetch data into their React applications.
In this beginners guide, you will learn how to consume RESTful API in React, including fetching, deleting, and adding data. We’ll also go over the two main ways to consume RESTful APIs and how to use them with React hooks.
Here’s an interactive scrim about consuming REST APIs in React (more scrims below):
What is a REST API?
If you’ve ever spent any time programming or researching programming, you’ve likely come across the term «API.»
API stands for Application Programming Interface. It is a medium that allows different applications to communicate programmatically with one another and return a response in real time.
Roy Fielding defined REST in 2000 as an architectural style and methodology commonly used in the development of internet services, such as distributed hypermedia systems. It is an acronym that stands for «REpresentational State Transfer.»
JSON is the most widely used file format because it is language-independent and can be read by both humans and machines.
How to Consume REST API’s in React
You can consume REST APIs in a React application in a variety of ways, but in this guide, we will look at two of the most popular approaches: Axios (a promise-based HTTP client) and Fetch API (a browser in-built web API).
In our case, we’ll be using functional components, which means that we need to use two major React Hooks:
- useEffect Hook: In React, we perform API requests within the useEffect() hook. It either renders immediately when the app mounts or after a specific state is reached. This is the general syntax we’ll use:
- useState Hook: When we request data, we must prepare a state in which the data will be stored when it is returned. We can save it in a state management tool such as Redux or in a context object. To keep things simple, we’ll store the returned data in the React local state.
Let’s now get into the meat of this guide, where we’ll learn how to get, add, and delete data using the JSONPlaceholder posts API. This knowledge is applicable to any type of API, as this guide is intended for beginners.
How to Consume APIs Using The Fetch API
The fetch() method requires a mandatory argument, which is the path or URL to the resource you want to fetch. Then it returns a Promise so you can handle success or failure using the then() and catch() methods.
A basic fetch request is very simple to write and looks like the below code. We are simply fetching data from a URL that returns data as JSON and then logging it to the console:
The default response is usually a regular HTTP response rather than the actual JSON, but we can get our output as a JSON object by using the response’s json() method.
How to Perform a GET Request in React With Fetch API
You can use the HTTP GET method to request data from an endpoint.
As previously stated, the Fetch API accepts one mandatory argument, which is true. It also accepts an option argument, which is optional, especially when using the GET method, which is the default. But for other methods such as POST and DELETE, you’ll need to attach the method to the options array:
So far, we’ve learned how things work, so let’s put everything we’ve learned together and perform a get request to fetch data from our API.
Again, we’ll be using the free online API JSONPlaceholder to fetch a list of posts into our application:
We created a state in the preceding code to store the data we will retrieve from the API so that we can consume it later in our application. We also set the default value to an empty array.
The major operation then occurred in the useEffect state, so that the data/posts are fetched as soon as the application loads. The fetch request yields a promise, which we can either accept or reject:
This response contains a large amount of data, such as the status code, text, and other information that we’ll need to have to handle errors later.
So far, we’ve handled a resolve using .then() , but it returned a response object, which isn’t what we want. So we need to resolve the Response object to JSON format using the json() method. This also returns a promise for us to get the actual data using the second .then() .
If we look at the console, we’ll see that we’ve retrieved 10 posts from our API, which we’ve also set to the state we specified earlier.
This is not complete because we have only handled the promise’s resolve and not the promise’s rejection, which we’ll handle using the .catch() method:
So far we have seen how to perform a GET request. This can be consumed easily into our application by looping through our array:
How to Perform a POST Request in React With Fetch API
You can use the HTTP POST method to send data to an endpoint. It works similarly to the GET request, the main difference being that you need to add the method and two additional parameters to the optional object:
The major parameters that might appear strange are the body and header.
The body holds the data we want to pass into the API, which we must first stringify because we are sending data to a web server. The header tells us the type of data, which is always the same when consuming REST API’s. We also set the state to hold the new data and distribute the remaining data into the array.
Looking at the addPost() method we created, it expects these data from a form or whatever. In our case, I created a form, obtained the form data via states, and then added it to the method when the form was submitted:
How to Perform a DELETE Request in React With Fetch API
You can use the HTTP DELETE method to remove data from an endpoint. It works similarly to the GET request, the main difference being the addition of the method:
This gets triggered when the button is clicked, and we get the id of the specific post in which the button was clicked. Then we remove that data from the entire retuned data.
This will be removed from the API but not immediately from the UI, which is why we have added a filter to remove the data as well. For each item in the loop, your delete button will look like this:
How to Use Async/Await in Fetch API
So far, we’ve seen how to make fetch requests normally using the promise syntax, which can be confusing at times. Then comes the chaining. We can avoid the .then() chaining by using Async/await and write more readable code.
To use async/await, first call async in the function. Then when making a request and expecting a response, add the await syntax in front of the function to wait until the promise settles with the result.
When we use async/await, all of our Fetch requests will look like this:
Here’s an interactive scrim to walk you through this:
How to Handle Errors with Fetch API
In this section, we’ll look at how to handle errors both traditionally and with async/await.
We can use the response data to handle errors in the Fetch API, or we can use the try/catch statement when using async/await.
Let’s look at how we can do this typically in Fetch API:
You can read more about Fetch API errors here.
And for async/await we can use the try and catch like this:
How to Consume APIs Using Axios
Axios is an HTTP client library based on promises that makes it simple to send asynchronous HTTP requests to REST endpoints. This endpoint in our case is the JSONPlaceholder Posts API, to which we will make GET , POST , and DELETE requests.
Here’s an interactive scrim that’ll walk you through the steps as you read:
How to Install and Configure an Axios Instance
Axios, unlike the Fetch API, is not built-in, so we will need to incorporate it into our project in order to use it.
You can add Axios to your project by running the following command:
Once you’ve successfully installed Axios, we can proceed to create an instance, which is optional but recommended as it saves us from unnecessary repetition.
To create an instance, we use the .create() method, which we can use to specify information such as the URL and possibly headers:
How to Perform a GET Request in React With Axios
We will use the instance we declared earlier for to perform the GET request. All we will do is set the parameters, if any, and get the response as JSON by default.
Unlike the Fetch API method, no option is required to declare the method. We simply attach the method to the instance and query it.
How to Perform a POST Request in React With Axios
As previously stated, you can use the POST method to send data to an endpoint. It functions similarly to the GET request, with the main difference being the requirement to include the method and an option to hold the data we are sending in:
How to Perform a DELETE Request in React With Axios
We can perform delete requests using the delete method, which gets the id and deletes it from the API. We’ll also use the filter method to remove it from the UI, as we did with the Fetch API method:
How to Use Async/Await in Axios
So far, we’ve seen how to make Axios requests using the promise syntax. But now let’s see how we can use async/await to write less code and avoid the .then() chaining.
When we use async/await, all of our Axios requests will look like this:
How to Handle Errors with Axios
For promise-based Axios requests, we can use the .then() and. catch ( ) methods, but for async/await, we can use the try. catch block. This is very similar to how we implemented the Fetch API, and the try. catch block will look like this:
You can read more about handling errors with Axios here.
Fetch API vs Axios
You may have noticed some differences, but let’s put them in a handy table so we can compare Fetch and Axios properly.
These distinctions will help you decide which method to use for a specific project. Among these distinctions are:
|Axios is a standalone third-party package that is simple to install.||Fetch is built into most modern browsers. No installation is required as such.|
|Axios uses the data property.||Fetch uses the body property.|
|Axios data contains the object.||Fetch’s body has to be stringified.|
|When the status is 200 and the statusText is ‘OK,’ the Axios request is accepted.||Fetch request is ok when response object contains the ok property.|
|Axios performs automatic transforms of JSON data.||Fetch is a two-step process when handling JSON data- first, to make the actual request; second, to call the .json() method on the response.|
|Axios allows cancelling request and request timeout.||Fetch does not.|
|Axios has built-in support for download progress.||Fetch does not support upload progress.|
|Axios has wide browser support.||Fetch is only compatible with Chrome 42+, Firefox 39+, Edge 14+, and Safari 10.1+. (This is known as Backward Compatibility).|
In this guide, we learned how to consume REST APIs in React using either the Fetch API or Axios.
This will help you get started with API consumption in React, and from there you will be able to consume data in more complex ways and manipulate your APIs however you choose.
Embark on a journey of learning! Browse 200+ expert articles on web development. Check out my blog for more captivating content from me.
Оптимизация подключения REST API для React приложений
Инструкция направлена на оптимизацию способа обмена данными между клиентом и сервером.
При написании компонентов, большинство разработчиков нагружают свой код излишней логикой в процессе подключения REST API. В итоге компонент в большей степени выполняет роль прокси объекта, и становится тяжело читаемым, трудно тестируемым, что особо пагубно влияет на разработку в команде. Основная задача инструкции направлена на обеспечение более декларативного подхода. Ниже представлены два варианта: код оптимального подключения REST API и классический способ для одного и того же компонента.
В первом случае мы используем уникальный объект «API», который сам выполняет все за нас, обеспечивает более полиморфный подход и переносит логику в общий модуль. Во втором примере присутствует большая логическая нагрузка на компонент.
Информация актуальна для React и React-Native приложений.
Однако существует множество других шаблонов обмена бизнес данными между клиентом и сервером с использованием сторонних библиотек. Некоторые из них:
Предполагается, что на данном этапе уже существуют конкретные эндпоинты для запроса на сервер.
Требуется создать отдельный модуль с названием API.
Он будет содержать конструктор класса, экспорт его экземпляра, методы для взаимодействия с сервером и состоянием приложения.
Далее требуется наполнить класс асинхронными методами для взаимодействия с сервером.
В данном примере добавлены два метода «getCartList» (получение списка данных) и «addProduct» (добавление данных на сервер). В результате вызова оба метода возвращают Promise, которые при выполнении возвращает ответ сервера в JSON формате.
Далее требуется добавить взаимодействие с состоянием приложения через redux store. Предполагается, что уже подключен redux \ modx (В примере redux). Добавим dispatch метод, и будем его вызывать внутри наших методов (можно вызвать dispatch до запроса для мгновенного обновления состояния, либо после запроса, когда требуется использовать данные из ответа сервера.)
В результате мы можем использовать наш класс внутри компонентов множества компонентов. При модификации api запроса потребуется изменить лишь логику внутри одного из методов класса внутри нашего модуля.
При этом мы можем вызывать методы класса с использованием своих хуков, например:
В момент отрисовки компонента, происходит вызов метода API и происходи загрузка данных и смена состояния. Данный подход очень напоминает хуки graphql-apollo.
Благодаря данному подходу мы избавляемся от большой логической нагрузки и уменьшаем зону ответственности при подключении и модификации api.
Сплошными стрелками на схеме отмечена основная логическая нагрузка при подключении.
Данный паттерн можно модифицировать благодаря использованию библиотеки axios .
Основная польза от использования библиотеки:
- Доступ к Interceptors (перехват запросов и ответов через обработчик)
- create метод для создания универсального интерфейса запросов.
- Оптимизация обработки ответа сервера для text и json формата.
- Удобная типизация.
Для защиты типов и поддержки кода добавим типизацию с применением typescript. Такой подход позволит получить доступ к результатам ответа сервера без документации к API сервера спустя длительное время работы с кодом.
REST APIs — How To Connect Your React App Like The Pros Two Approaches Including A Detailed Example
Consuming REST APIs with React isn’t very difficult. It only takes a useEffect plus a few lines of code and you have the data in your frontend.
At least it seems like that at first.
But once you start adding features to your data fetching logic you quickly end up with a big mess of entangled spaghetti code.
Luckily there are a few libraries to our rescue (among them react-query). These not only make it really easy to fetch data but also deliver valuable and commonly used features right out of the box.
On this page, we’ll use an example project to see how quickly a “simple” approach to data fetching using useEffect can get out of hand. In contrast, we’ll see how easy it is to build advanced features using react-query by building a snappy paginated list component.
Nobody wants to read through the setup of a new React app, I assume. So I prepared a realistic project for this article that shows a slightly more advanced use case for data fetching.
It’s an error-tracking tool similar to Sentry that I created for the React Job Simulator. It includes a React / Next.js frontend and a REST API which we will get our data from.
The goal is to fetch data and render it in our frontend like this:
You can see that this list has several pages (note the “Previous” and “Next” buttons at the bottom). This is very common in real-world projects.
But currently, we only see a single issue that is hard-coded in our frontend code.
The code responsible for rendering the list looks like the following at the moment. The goal is to replace the hard-coded data with data fetched from the API.
To get our data we’ll use the REST endpoint prolog-api.profy.dev/issue (click the link to see the JSON response). You can find more details about this REST API in its Swagger API documentation.
This is the response for the first page.
The issue endpoint is a simple GET endpoint. But (as common in production APIs) this endpoint is paginated. That means that we don’t get all issues in a single request but only 10 at once (aka 10 per page). To get the next “page” of issues we simply append a query parameter like prolog-api.profy.dev/issue?page=2.
The endpoint also returns a meta object that contains information related to the pagination like the current page or the number of total pages.
The “Simple” Approach With useEffect
Let’s start with a “simple” approach that many beginner tutorials teach. We’ll see that it’s in fact very simple to get the data and render it in the UI. But as soon as we go into the nitty-gritty details things start to get complex.
Inside our component, we want to fetch the data during the initial render. With the useEffect hook this is simple:
When we refresh the app we now see the data logged in the console of our browser:
Next, we have to save the data in a state variable to render it in the UI.
And voila, we see the data in our app.
Easy enough, isn’t it?
Yes, just fetching data like that is easy. But in a production application, we can’t just leave it there. What if we wanted to handle loading and error state?
What if we wanted to add a route parameter for pagination and cancel previous requests when the page changes?
What if we wanted to add other features that are often a requirement in real-world applications? Like
You get where this is going. The initially simple-looking code can quickly become a nightmare of entangled spaghetti full of bugs. Believe me, in a previous job a few years ago we went down this road… and it wasn’t pretty.
The Effective Approach With react-query
Luckily since a few years, we have libraries that abstract a lot of common use cases of data fetching. Popular examples are react-query, Redux Toolkit Query, or Apollo. react-query is one of the most widespread and versatile ones so we use it for this tutorial.
To get an overview of all the advantages it is a good idea to have a look at the react-query documentation. But I think this screenshot says it all.
To use react-query we need to wrap our app component with a QueryClientProvider . This is common practice that you probably know from other libraries as well.
Additionally, we added the dev tools that come with react-query . We will talk about this further down the page.
A Simple Implementation
These simple three lines of code give us already a loading and error state.
The first parameter ["issues"] that we pass to useQuery is used as a unique identifier for the data stored in the cache. The second parameter is the callback that connects to the API (the same axios call that we used in the useEffect ). Optionally we can pass a third parameter with config options.
You can see that useQuery doesn’t actually fetch the data itself but is more like a wrapper around the data-fetching callback that adds additional functionality.
Advanced Features For Free
We saw at first glance that the loading and error states are supported out of the box. But under the hood, we also get a few really handy advanced features for free. Here are a few examples:
1. A request cache that automatically updates in the background
In this video, you can see that a request is sent on the initial page load (the loading screen is shown). But when we navigate to another page and back we don’t see the loading screen anymore. The data is immediately there because it was cached.
This isn’t all though. In the network tab at the bottom, you can see that a request is sent in the background. The cache is automatically updated with the newest server data.
2. Automatic updates of stale data
The cache is not only updated when we navigate back and forth inside the application. When the user switches to another tab and comes back to our app the data is by default updated in the background as well.
3. Dev tools
You may have seen the “flower” icon at the bottom left of the page? That’s the button to open the dedicated react-query dev tools. You can see exactly what kind of requests have been sent, what’s in the cache, and the data plus additional information for each request.
Very handy for debugging.
Custom Hook As Best Practice
Since data fetching logic is often shared between multiple components it’s best practice to extract custom hooks for each endpoint.
Now we can use this hook in our component.
Note that we have separated the fetching logic from the component now. In the previous simple example, the component used the response object from axios directly. Now we can easily encapsulate this in our custom hook. This can be beneficial, for example, if we want to replace axios with something else at some point.
To further improve our custom hook it’s common to extract the data fetching callback into its own function.
Now axios is further isolated and the getIssues function is responsible for handling the response data. Additionally, we can reuse the getIssues function in other parts of our app (e.g. the server for server-side rendering).
Usage With TypeScript
For type safety and improved developer experience, it’s easy to use TypeScript with react-query . Simply pass the type of the expected response data plus an error type to the useQuery generic.
The types in our case might look like this.
Now we not only have advanced errors and warnings in our IDE. We don’t even have to remember the data structure of our API data. The IDE’s autocomplete feature (here VS Code) takes care of that.
Advanced Example: A Paginated List Component With Great UX
Great, we saw a simple example of how to make a request to our REST API. We also already saw some of the hidden powers that come with react-query out of the box.
Now it’s time for a slightly more advanced example. We’ll implement the pagination for our issue list. At the surface, this only means adding a query parameter to the request URL. But as we’ll see in a bit there are some UX hick-ups that we’ll run into.
It’s magic how easily those are fixed just by adjusting a few react-query options.
Let’s start by adding pagination. In our useIssues hook we can simply add a parameter and append it to the URL.
Note that we also added the page parameter to the cache identifier of useQuery . You can compare it to the dependency array of the useEffect hook.
In our component, we can simply pass the page state variable to the useIssues hook and connect it to the pagination buttons.
The pagination works already well. When clicking on the buttons at the bottom able to see the different pages of issues as expected. As a bonus, thanks to the automatic caching the user doesn’t even see a loading screen when navigating to a previous page.
But as you can see the loading state that is shown when clicking on the “Next” button is pretty annoying. The table disappears completely until the new data arrives.
It would be great if we could show the old data until the next page is loaded. And no surprise, this is very simple: we just need to add an option to useQuery .
The loading state is gone! The table doesn’t disappear anymore.
This looks already a lot nicer. But the drawback is the delay between the button click and the next page being rendered. Depending on how fast the request resolves the user might not understand that the button has any effect and fall into “click rage”.
Prefetching The Next Page
But of course, react-query is there to the rescue. The docs have a section about prefetching data so that the user doesn’t have to wait. Here is the code from the docs:
Even better, the docs (can’t praise them enough) provide examples for common use cases. Among them an example implementation for pagination that includes prefetching the next page. Let’s adapt that example to our application:
This makes paginating basically instantaneous. There’s no delay for the user (as long as the prefetch request has been resolved obviously).
When you look at the network requests at the bottom you can see how the next page is already loaded upfront.
Reducing The Number Of Requests By Adjusting The Stale Time
There’s a last small issue though: when clicking the “Next” button there are two requests. On the initial page load there are two requests as expected (page 1 and 2). But when we click on the “Next” button there are again two requests (page 2 and 3).
The reason is that react-query refetches “stale” data automatically. But for our kind of app, the data doesn’t go stale that quickly so the duplicate request for page 2 isn’t necessary. The additional requests may increase our server costs so we can as well get rid of them.
The solution is simple: we add another option called staleTime that we pass to useQuery .
Now we only see one request for the second page. We got rid of the unnecessary request.
While useEffect combined with fetch or axios seems like a simple solution at first you risk ending up with a whole bunch of unmaintainable spaghetti code.
Modern React projects use libraries to outsource the complexities of common use cases in data fetching. react-query is currently one of the most popular data-fetching libraries. And you have seen its power.
With only a few lines of code and a few config options, we were able to create a snappy paginated list with fairly good UX.
Still, we’ve seen only a fraction of what’s possible with react-query . Check out the awesome docs for many more features.