/useLoader-custom-hook

Custom hook to display a loader image/gif while data is being fetched

Primary LanguageJavaScript

UseLoader

This may be my first contribution to the frontend community. This repository contains the custom hook useLoader . It is basically providing the functionality to display the loader component of your choice while the data is being fetched. Following is the explanation of usage and working.

Usage

useLoader(), in its arguments, is taking just an object with the following properties:

  1. requestURL: URL of the API endpoint from where the data is to be fetched and meanwhile the loader is displayed (URL to where the request is to be sent).
  2. requestMethod: The request method type can be either 'GET' or 'POST' only.
  3. requestPayload: It can be whatever payload/data that we want to sent along with request if the requestMethod is selected as 'POST'. It is useless if the 'requestMethod' is selected as 'GET'
  4. onSuccess: The value of this property is a function that we would like to run if the response of the request is successful. This function will get that response itself in its only argument that can be used inside the body of the function for further processing. This is an optional property.
  5. onFailure: The value of this property is a function that we would like to run if the response of the request is not successful. This function will get the error itself in its only argument that can be used inside the body of the function for further processing. This is an optional property.

In return, the hook will give following four things packed inside an array:

  1. loading: This is the boolean variable. true value suggests that fetching of the data and then, if provided, the execution of onSuccess() or onFailure(), are still in progress - so that meanwhile we can display the loader of our choice, whereas false suggests that they are completed and we can now stop displaying the loader.
  2. response: This is the response itself that we get in return of the request. If the request wasn't successful, response will be null, otherwise we can extract the needed data from it.
  3. error: This is the error itself that we get in return of the request. If the request was successful, error will be null, otherwise we can extract the error related data from it.
  4. getData: This is a function which we would like to run in our functional component whenever we need to fetch the data and display the loader meanwhile. In each execution of the function, loading will set to true and the request will be made according to the values of requestURL, requestMethod and requestPayload. Then, it will also execute onSuccess() or onFailure() functions if they are provided. At the end it will set loading back to false.

Example

Let's say we have a simple page which has a button Get Users on which if we click, the data of few users will be fetched. While fetching we will simply display "Loading..." string instead of any loader photo or gif or any complex component. When we are done fetching we will just display their names as a list.

App.js

import { useState } from "react"
import "./App.css"
import UserList from "./Components/UserList"

function App() {
	const [showData, setShowData] = useState(false)
	return (
		<div className='App'>
			{showData ? (
				<UserList />
			) : (
				<button onClick={() => setShowData(true)}>Get Users</button>
			)}
		</div>
	)
}
export default App

It has a button intially displayed. On clicking the button, it will disappear and we will be able to see the UserList.

UserList.js

import React, { useEffect } from "react"
import useLoader from "../CustomHooks/useLoader"

const requestURL = "https://jsonplaceholder.typicode.com/users"
const requestMethod = "GET"
const doSomethingWithResponse = (response) => {
	console.log(
		"Hello from onSuccess()!!" +
			"\nHere you can also dispatch custom action objects using the returned response of the request" +
			`\n\nresponse:\n${JSON.stringify(response.data)}`
	)
}
const doSomethingWithError = (error) => {
	console.log(
		"Hello from onFailure()!!" +
			"\nHere you can also dispatch custom action objects using the returned error of the request" +
			`\n\nerror:\n${JSON.stringify(error.message)}`
	)
}

function UserList() {
	/* USE Of useLoader() hook */
	const [loading, response, error, getUsers] = useLoader({
		requestMethod,
		requestURL,
		onSuccess: doSomethingWithResponse,
		onFailure: doSomethingWithError
	})

	useEffect(() => {
		getUsers()
	}, [])

	const userData = response && response.data
	const errorMsg = error && error.message

	return (
		<div>
			<h1>This is UserList</h1>
			{loading ? (
				<h2>Loading...</h2>
			) : errorMsg ? (
				<h2>{JSON.stringify(errorMsg)}</h2>
			) : (
				<div>
					{userData &&
						userData.map((user, index) => (
							<p key={index}>{user.name}</p>
						))}
				</div>
			)}
		</div>
	)
}
export default UserList

In this file, firstly there are properties (requestURL, requestMethod, doSomethingWithResponse, doSomethingWithError) defined for the object that will be passed to useLoader hook.

In UserList, the object with the necessary properties is passed to the useLoader() and the returned values are stored in [loading, response, error, getUsers]. Note that here getUsers will be the function getData which is being returned from hook.

Then using useEffect(), a call getUsers() is made for just one time only when the component gets mounted. With that, loading will be set to true, a request will be made to the API to get the list of data of users and render() will display the string "Loading...". When the successful response or the error will be received, user-data or error-message will be extracted respectively before next execution of render(), loading will be set back to false, string "Loading..." will be removed from the DOM and then accordingly the user-data or error-message will be displayed in the next render.

Note: If we want to dispatch any action objects inside onSuccess() using response-data as payload or inside onFailure() using error-data as payload, dispatch has to be provided to these functions before passing them to the hook. In such scenario, following steps can be followed:

  • Modify the functions arguments
const doSomethingWithResponse = (response, dispatch) => {
	/* USE of dispatch
	dispatch({
		type: SET_USERS,
		payload: response.data
	})
	*/
}
const doSomethingWithError = (error, dispatch) => {
	/* USE of dispatch
	dispatch({
		type: SET_ERROR,
		payload: error.data
	})
	*/
}
  • Create a reference dispatch using useDispatch()
const dispatch = useDispatch()
  • Set onSuccess and onFailure as the arrow functions with response/error and inside, call the doSomethingWithResponse and doSomethingWithError with dispatch as its second argument.
/* USE Of useLoader() hook */
const [loading, response, error, getUsers] = useLoader({
	requestMethod,
	requestURL,
	onSuccess: (response) => doSomethingWithResponse(response, dispatch),
	onFailure: (error) => doSomethingWithError(error, dispatch)
})

While using the redux-store in the application, after the completion of data-fetching, it is highly recommended to set/update the data into the redux-store first by dispatching the action objects from onSuccess() or onFailure(), utilizing the response-data or error-data as payload respectively, then use the store-data to render the components through props by defining the mapStateToProps() and passing the state-slices into the props.

Such way of using useLoader hook is demonstrated by this example which is a bit more complex than the above one.

Middlewares

The hook is maintaing its own small redux-store and following two middlewares are being used along with it.

  1. redux-thunk: Because of the thunk middleware, we can have action creators that return a function instead of an action object. Since such action creators doesn't need to be pure, it can have side-effects also and that's what the advantage is utilized by useLoader hook.
  2. redux-logger: This is to log the actions being dispatched by the useLoader hook in the console during the production in order to understand/keep track of the flow of actions being dispatched. It is obvious to suggest that this middleware should be removed before deploying the application for the actual use.

Reference

Code Evolution

Suggestions are invited✨