Seamless API Requests with React Hooks — Part 1

Jaryd Carolin
3 min readOct 26, 2018

--

All the custom hooks you’ll soon have in your React toolkit - credit rawpixel on Unsplash

React Hooks were announced recently so I wanted to demonstrate one really common use case where they can dramatically simplify things for us. From thunks to sagas, communicating with your API and integrating that data into your React components can be a challenge.

React Hooks allow us to do state and effect management within our function components. The provided hooks are great for common use cases but the most powerful feature is the ability to use them to create your own custom hooks. We’re going to create custom hooks for two types of requests:
- Part 1: Request dependent on a state variable
- Part 2: Request triggered by a UI action

Before we start writing the custom hook, let’s setup how we want this to work from our function component. We’ll be looking to pass a request object in, something that could be sent with axios, and get back some type of response related to what we’ve requested. For this example we will get a list of todos like so:

function App(props) {
const todos = useEndpoint({
method: 'GET,
url: '/todos',
});
}

Let’s get stuck into it. We’ll import the required functions from the react package. Our endpoint will take an object, a request req, that we intend to call.

import React, { useState, useEffect } from 'react';function useEndpoint(req) {
// good stuff goes here
}

Firstly we need decide how we want the state of our request represented in our component. Because of the asynchronous nature we probably want this representation to be quite detailed. Ideally we’ll know if the request has failed, succeeded or is still waiting for a response.

function useEndpoint(req) {
const [res, setRes] = useState({
data: null,
pending: false,
completed: false,
error: false,
});
}

We want this request to run somehow, which is what we’ll use useEffect for. Inside the effect we will use setRes to indicate the state the request is in.

function useEndpoint(req) {
const [res, setRes] = useState({
data: null,
pending: false,
completed: false,
error: false,
});

useEffect(() => {
setRes({
data: null,
pending: true,
completed: false,
error: false,
});
axios(req)
.then(res =>
setRes({
data: res.data,
pending: false,
error: false,
complete: true
}),
)
.catch(() =>
setRes({
data: null,
pending: false,
error: true,
complete: true
}),
);
}, []);
return res;
}

Inside the effect we follow the request through three potential transitions, the commencement of the request followed by either the successful completion or the failure provided by the response.

As it stands this request will only be fired off once however, because we have passed in an empty array to the second argument of useEffect. Let’s make it dynamic so the hook adapts to changes in the URL (at a minimum, you could always analyse the request object to a deeper extent). This is achieved by passing in [req.url] as the second argument.

By creating this dependency we can have other stateful pieces of data influence our requests and they will update automatically. The full code and a demo is on CodeSandbox, linked below.

Flick through all the todo objects using the “Next Todo” button
import React, { useState, useEffect, useRef } from "react";
import axios from "axios";
function useEndpoint(req) {
const [res, setRes] = useState({
data: null,
complete: false,
pending: false,
error: false
});
useEffect(
() => {
setRes({
data: null,
pending: true,
error: false,
complete: false
});
axios(req)
.then(res =>
setRes({
data: res.data,
pending: false,
error: false,
complete: true
}),
)
.catch(() =>
setRes({
data: null,
pending: false,
error: true,
complete: true
}),
);
},
[req.url]
);
return res;
}
export default function App() {
const todosApi = "https://jsonplaceholder.typicode.com/todos";
const [count, setCount] = useState(1);
const todo = useEndpoint({
method: "GET",
url: `${todosApi}/${count}`
});
return (
<div>
<button onClick={() => setCount(count + 1)}>Next Todo</button>
<h1>Todo {count}</h1>
<div>
{(todo.pending && 'Loading...') ||
(todo.complete && todo.data.title)
}
</div>
</div>
);
}

In Part 2 we’ll use the same structure to send a POST request for creating a new todo.

Let me know your thoughts!

EDIT: Part 2 is now available right here.

--

--