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.
useLoader()
, in its arguments, is taking just an object with the following properties:
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).requestMethod
: The request method type can be either'GET'
or'POST'
only.requestPayload
: It can be whatever payload/data that we want to sent along with request if therequestMethod
is selected as'POST'
. It is useless if the'requestMethod'
is selected as'GET'
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.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:
loading
: This is the boolean variable.true
value suggests that fetching of the data and then, if provided, the execution ofonSuccess()
oronFailure()
, are still in progress - so that meanwhile we can display the loader of our choice, whereasfalse
suggests that they are completed and we can now stop displaying the loader.response
: This is the response itself that we get in return of the request. If the request wasn't successful,response
will benull
, otherwise we can extract the needed data from it.error
: This is the error itself that we get in return of the request. If the request was successful,error
will benull
, otherwise we can extract the error related data from it.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 totrue
and the request will be made according to the values ofrequestURL
,requestMethod
andrequestPayload
. Then, it will also executeonSuccess()
oronFailure()
functions if they are provided. At the end it will setloading
back tofalse
.
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
usinguseDispatch()
const dispatch = useDispatch()
- Set
onSuccess
andonFailure
as the arrow functions with response/error and inside, call thedoSomethingWithResponse
anddoSomethingWithError
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.
The hook is maintaing its own small redux-store and following two middlewares are being used along with it.
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 byuseLoader
hook.redux-logger
: This is to log the actions being dispatched by theuseLoader
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.
Suggestions are invited:sparkles: