Introduction
Before now; it was pratically impossible to have a state in a functional component then comes React Hooks.
What is React Hooks?
I can not really give a specific definition to react hooks but what I can say is that, react-hooks provides functional API which makes it possible for us to use state(s) in a functional component
In a lay-man language: React hooks makes it possible to define states in a functional component using it's APIs.
We have different React Hooks (I will be covering this in another tutorial), but for now what i hope to achieve in this is to explain how to create a custom react-hooks;
A custom react hooks is a functional component that is built with the fundamental react hooks api (useState, useEffect etc); one of the ways to know a custom react hooks is by its name, which start with use eg useAPi
In this tutorial we'll be extending the api resource file by creating a custom react hooks which will help us handle the various 'states'/'life-cycle' while calling an API
basically for all APIs - the various things which are important to track are
data - the result returned after calling the endpoint
error - in a situation where there's a client or server error, we should be able to track the error returned
loading - the current state i.e. if it still processing or not
status code - the response status code from the server
Example of a custom-hook (useApi)
import { AxiosError } from 'axios';
import { useState } from 'react';
import { api } from 'utils'; // from https://akinolu52.hashnode.dev/creating-an-api-resource-file-using-axios
interface connectProps {
method: 'get' | 'post' | 'patch' | 'delete' | 'put';
values: object;
url: string;
}
function useApi() {
const [loading, setLoading] = useState<Boolean>(false);
const [data, setData] = useState<Record<string, any> | null>(null);
const [error, setError] = useState<AxiosError | null>(null);
const connect = async ({ method, url, values }: connectProps) => {
try {
setLoading(true);
let result = null;
if (method === "get" || method === "delete") {
result = await api[method](url);
} else {
result = await api[method]([url, values]);
}
setData(result);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
return { data, error };
}
return { connect, loading };
}
export default useApi;
useApi.tsx
Explanation
import { useState } from 'react';
import { api } from 'utils';
In the first line; I import useState which will be used to manage the local state of the custom hooks and then I import the api from utils which was previously implemented in this tutorial
interface connectProps {
method: 'get' | 'post' | 'patch' | 'delete' | 'put';
values: object;
url: string;
}
This is the interface for the useApi-connect function (basically the types definitions of data to be passed to the function), here the connectProps included
- method: the request type
- values: the request data
- url: the request url
function useApi() {}
Notice that the function name start with use which signifies that we are in a custom hook
const [loading, setLoading] = useState<Boolean>(false);
const [data, setData] = useState<Record<string, any> | null>(null);
const [error, setError] = useState<Record<string, any> | null>(null);
This are the custom hooks states, these states handles the loading, data and error information for the api being called.
- loading: this is a boolean value which is used to track the current state of the api
- data: this is used to hold the result of a successful API call eg response code 'OK'
- error: this is used to handle the error information of the api, this error state will not be empty if an error occur while processing the api call
const connect = async ({ method, url, values }: connectProps) => { ... }
This is the function that handles the API call, this function is called with the following parameters: method, url, values
- method: the request type
- values: the request data
- url: the request url
try {
setLoading(true);
let result = null;
if (method === "get" || method === "delete") {
result = await api[method](url);
} else {
result = await api[method]([url, values]);
}
setData(result);
}
In this block of code, the loading state is set to true, so as to signify that the api is in progress (will be processed)
for method get and delete, these api does not involve sending data to the backend so the api is only processed with the url
for the following methods: post, put and patch; certain data are sent to the to the backend server for processing hence the values are sent with the url in an array to the api function
the result of any of these request type is stored in the data state
catch (error) {
setError(error);
}
This block handles an error, all it does is to store the error received as the response to the error state
finally {
setLoading(false);
}
This block handles resetting of the loading state, i.e set the loading state to false which is the initial state.
How to use
import { yupResolver } from "@hookform/resolvers/yup";
import { Button, Input } from "components";
import { SubmitHandler, useForm } from "react-hook-form";
import { loginSchema } from "schemas";
import useApi from "useApi";
type FormData = {
email: string;
password: string;
};
function LoginForm() {
const { connect, loading } = useApi();
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormData>({
resolver: yupResolver(loginSchema),
});
const onSubmit: SubmitHandler<FormData> = async (data) => {
const { data, error } = await connect({
method: "POST",
url: 'https://some-fake-api.com/login',
values: data
});
console.log('response from api: ', data, error);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div className="mb-6">
<Input
type="email"
label="Email Address"
{...register("email")}
error={errors?.email}
/>
</div>
<div className="mb-6">
<Input
type="password"
label="Password"
{...register("password")}
error={errors?.password}
/>
</div>
<div className="mb-3">
<Button text="Login" type="submit" isLoading={loading} />
</div>
</form>
);
}
export default LoginForm;
LoginForm.tsx
Footnotes
Thanks, for checking out this tutorial. (please like and add your comments) You can also check out my other tutorials on my blog
If you have any questions, feedback or comments, please let me know.
You can connect with me on twitter email github
You can also reach out to me(I do React-Native & React Js) twitter email