Fetch data in React using Hooks

@3sh
5 min readFeb 22, 2021

In this post we’ll see how to fetch data using Promises and async/await in a React application.

  1. We will first see how we can fetch data in react using hooks.
  2. Followed by a discussion on how to fix regeneratorRuntime error while using fetch or async/await. Although create-react-app supports async/await out of the box. Using customised webpack configurations may result in regeneratorRuntime errors.

When a JavaScript date has gone bad, “Don’t call me,I’ll call you. I promise!” — anonymous

About Promises

The giggle mentioned above explains, the nature and behaviour of what promises are and what their existence means. Life before Promise was not considered trustworthy especially for data fetches and async activities were a a game of anticipation.

Concept of Promises changed the uncertainty to dependability in the world of single threaded language. Promises in JavaScript are like the most dedicated and loyal friends of functions, who call them. As the name suggest they provide a layer of confirmation to whether the event would result into a success or would fail or what the current situation is. By which the user can now perform different actions based on the response they get in return.

Promises in React

React component provides lifecycles which provides a breakpoint for users to perform tasks during different phases of lifecycle. The most famous and most used lifecycle componentDidMount invites for making data fetch calls. Although the syntax for fetching data in a componentDidMount and useEffect hook would remain the same, the implementation in hooks would differ.

We would be taking a look at how the promises would be used in hooks as it requires few additional things to be considered while making a call through them, as compared to those in componentDidMount which is pretty straight forward.

A little about UseEffect hook

Effects in React provides a way to interact with Functional Components. We can perform operations(side effects) on the component through Effects. useEffect takes two parameters first being the callback function and later being an array which represents the dependency list. A callback function is called when the effect takes place, but the dependency list is something which acts like observables. All the variables mentioned as a part of the dependency list are observed for changes. If incase any of those variables undergo changes the callback function would be called. This makes the Effect somehow act like the componentDidMount when empty, componentDidUpdate when not specified and something more interesting like observables when you mention them as a part of dependency list.

Fetch using fetch

Let’s check out how to write a simple ajax call using fetch. Fetch returns promises and we would see how we can handle situations for success as well as errors during the response.

React.useEffect(() => {
fetch("/todoapp/todos.json")
.then(resp => resp.json())
.then(data => {
// handle on success
})
.catch(err => {
// handle on error
});
}, []);

In the above example, we have handled Promises with .then chaining. Promises provide a way to handle success by calling the callback function passed within the “then” function. While .catch helps in handling the error.

Points to take away from above example

  1. Ajax call made through fetch or any other libraries like axios, implement Promises as default.
  2. Response from Promises can be handled through the .then and .error functions, on success / error respectively.
  3. As we have passed empty dependency list the fetch would only be called during the initial Mount.
  4. To call the data during change of a variable you can add the variable in the dependency list.

Fetch using async/await

async/await is a syntactic sugar over promises. It makes it easier to adapt the usage of promises in our code. The syntax is easy to use and understand. Let’s take the similar example to see how async/await would be used with above situation.

const [todos, setTodos] = React.useState([]);React.useEffect(() => {
let fetchTodos = async (state) => {
let resp = await fetch("/todoapp/todos.json");
let data = await resp.json();
setTodos(data.todos);
};
fetchTodos();
}, []);

The above example does the same thing what we were trying to do in the previous example. Using async/await makes this above code look simpler comparatively.

We create a function with an async keyword. This makes us use the await in the functional body for letting the execution of the function halt till the fetch is done. Mind that the execution of the tasks within the function is on halt not the other tasks outside the async function.

await waits till the response is received from the server. And will proceed post that. This makes us use them when circumstances requires us to handle logic synchronously. This makes us sound why is the function named async? funny right. There is a reason behind it. please check Is async + await = sync?

From ES2020 onwards, await can now be used outside the async functions as well. Until which the await is only used within the async function. For more info check the this.

Handle errors within async/await

It is a good practise to handle errors within your code. With .then we do get a .catch function which is called during an error. While in the async/await we do not get such pre-implementation handlers. This is where we can go the classic way of using try-catch.

const [todos, setTodos] = React.useState([]);React.useEffect(() => {
let fetchTodos = async (state) => {
try {
let resp = await fetch("/todoapp/todos.json");
let data = await resp.json();
setTodos(data.todos);
} catch(error) {
throw new Error(error)
}
};
fetchTodos();
}, []);

regeneratorRuntime is not defined?

In case you come up with this issue, which normally would occur while run or webpack. The solution would be pretty simple.

npm i @babel/preset-env @babel/plugin-transform-runtime @babel/runtime --save-dev

Create / Edit the .babelrc / babel.config.json as prescribed

{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-transform-runtime"
]
}

*This solution worked well for me and is my personal thought over solving the regeneratorRuntime issue with async/await. Although there are different solutions mentioned over the net looking over different circumstances.

About Me

I am the author of Rootz JS and we call ourself rootzies.

Rootz as a community is growing fast. We are expecting like-minders Rootzies to join us on making a product which can make an impact. We have a roadmap with major destinations. And all these destinations carry a vision to change the way we look at our application today.

If you feel you can make an impact, you are welcome. Because we know you certainly will :)

Please STAR, FOLLOW and FORK us.

blogs: http://rootzjs.medium.com
github: http://github.com/rootzjs
npm: http://npmjs.com/package/@rootzjs/core
discord: http://discord.gg/RnDRtD7faF
linkedin: http://linkedin.com/company/rootzjs

Happy Coding Rootzies !!

--

--

@3sh

Love to scribble about JavaScript and React JS. Been almost a decade with the tech. I feel i can be of some help to those looking forward on these buddies.