Optimizing data fetching in React
Faster data access enhances the business application usage with better user experience. Reducing the response time while fetching data from the server to update specific sections of a webpage has a huge impact on the performance and behavior of sites.
When a user logs in or accesses an application, they expect a certain amount of data to be available to them in no time – data like sections of a webpage, a popup window, keywords to begin a search or a dropdown menu. ‘Fetching data’ is the default operation in any web application to display everything you require on initial page load. Though it may sound simple, performing fetches to multiple endpoints adds another layer of complexity to develop the application.
Usually, a developer builds patterns on how and when the data needs to be fetched through the network to dynamicize the page. In this blog, you can learn about those data fetching patterns used in React applications and know the best alternative.
The async-await route to clearer code
The above code compares the implementation of async-await with the traditional promises. Any function prefixed with the keyword async is expected to return a promise. It can also use the keyword await, making the code lines next to the keyword await a blocked code until the promise resolves or rejects it. This makes the code a lot more intuitive and gives the maintainer an easy flow while trying to understand the implementation.
As we have seen the modern language approach of fetching the data via async and await, let us now see how we can use these in React. We will also see examples of implementing React and Redux paradigm.
How do we generally code in React?
When any request is made, we see it in two phases:
When the data is loading, we show the user a kind of spinner; once loaded, we show the user data/ error.
The above screenshots showcase the pseudo code of a component when trying to fetch the data on load. Till then, it shows “loading” and later shows the data.
How do we perform data fetching using Redux?
Redux is one of the state management tools for React. It helps us to maintain an application-level data store and let individual components share the data among themselves.
Reducers in Redux are meant to be pure functions, so they cannot hold a place for any promises to be able to fetch the data there, due to which we introduce middlewares. The popular middlewares for data fetching are redux-thunk and redux-sagas. In sagas, our data fetching code listens to every dispatch and is triggered on a specific action. The saga fetches the data and triggers a success action to be able to save the data in the store.
Data flow in Redux
Key data fetch challenges using Redux
The previously mentioned ways to fetch data can be repetitive and often there are some pain points faced by the developer:
- Too much coding – The boilerplate code to setup and maintain the Redux environment involves too much coding.
- Too much complexity – In the workflow shown in the screenshots above, it’s fairly difficult for a beginner to understand the connection between those files.
- Too many files to edit – In order to add a new API call, we will have to edit 4 files at least.
- Duplicate code – The loading and loaded states need to be maintained among individual components.
The Solution – Custom Hooks
To resolve the pain points, we need to first generalise different scenarios in the application where we make an API call.
- Fetch on Render – The scenarios where we render any page data once we load the page
- GET Data
- Fetch on Action – The scenarios where we perform any action like a form submit
Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.
An added advantage of Hooks is that we can create a custom Hook holding the state-specific code and reuse those Hooks among different components. Using this point, let’s see how we design our custom Hooks for data fetching.
- Fetch on Render
- GET Data – useGet()
- Fetch on Action
- POST – usePost()
- PUT – usePut()
- DELETE – useDelete()
- GET – useLazyGet()
The below is a sample pseudo code, which illustrates a custom Hook using axios, but we can even use fetch normally.
The Hook takes in a parameter for path, which is the url for the request. It holds the loading and error state. Finally, the Hook returns an object with data, the loading and error states. It can be implemented inside a component in the following way:
For scenarios where we need to trigger the API on an action, the Hook doesn’t use useEffect to trigger the fetchData function, but it returns the fetchData function to the calling component. My component can fetch data depending on the required event trigger.
Traditional data fetch vs useGet
The above screenshot code shows the difference between a traditional data fetch and useGet implementation. The latter clearly involves less code and is more intuitive. The components now don’t have to bother about maintaining the loading states and can focus on handling the outputs.
The Hooks edge
Generalised custom Hooks helps a developer in the following ways:
- It helps in implementing codes faster.
- Developers can write unit tests separating the concerns of UI with data using Hooks.
- Since there is less custom code required for each component, Hooks also help in better maintenance.
For developers, using Hooks is a better alternative than its counterparts to optimize data fetching in React. Give it a spin.
- Promise: It allows you to associate handlers with an asynchronous action’s eventual success value or failure reason
- Async: The word async before a function means one simple thing: a function always returns a promise. Other values are wrapped in a resolved promise automatically
- Loading State: It is the state when you see a spinner on the web page, while you are waiting for the data to load
- Loaded State: This state infers when the spinner indicating the loading state is over and you start seeing the data