Our Global Presence
Canada
57 Sherway St,
Stoney Creek, ON
L8J 0J3
India
606, Suvas Scala,
S P Ring Road, Nikol,
Ahmedabad 380049
USA
1131 Baycrest Drive,
Wesley Chapel,
FL 33544
In this post, we will cover five simple React hooks that you will find handy in any project. These hooks are useful no matter the features of the application. For each hook, we will provide the implementation and the client code sample.
Web applications use modals extensively and for various reasons. When working with modals, you quickly realize that managing their state is a tedious and repetitive task. And when you have code that’s repetitive and tedious, you should take time to abstract it. That’s what useModalState
does for managing modal states.
Many libraries provide their version of this hook, and one such library is Chakra UI. If you want to learn more about Chakra UI, check out my blog post here.
The implementation of the hook is very simple, even trivial. But in my experience, it pays off using it rather than rewriting the code for managing the modal’s state each time.
import React from "react"; import Modal from "./Modal"; export const useModalState = ({ initialOpen = false } = {}) => { const [isOpen, setIsOpen] = useState(initialOpen); const onOpen = () => { setIsOpen(true); }; const onClose = () => { setIsOpen(false); }; const onToggle = () => { setIsOpen(!isOpen); }; return { onOpen, onClose, isOpen, onToggle }; };
And here’s an example of client code using the hook:
const Client = () => { const { isOpen, onToggle } = useModalState(); const handleClick = () => { onToggle(); }; return ( <div> <button onClick={handleClick} /> <Modal open={isOpen} /> </div> ); }; export default Client;
useConfirmationDialog
is another modal-related hook that we use quite often. It’s a common practice to ask users for confirmations when performing sensitive actions, like deleting records. So it makes sense to abstract that logic with a hook. Here’s a sample implementation of the useConfirmationDialog
hook:
import React, { useCallback, useState } from 'react'; import ConfirmationDialog from 'components/global/ConfirmationDialog'; export default function useConfirmationDialog({ headerText, bodyText, confirmationButtonText, onConfirmClick, }) { const [isOpen, setIsOpen] = useState(false); const onOpen = () => { setIsOpen(true); }; const Dialog = useCallback( () => ( <ConfirmationDialog headerText={headerText} bodyText={bodyText} isOpen={isOpen} onConfirmClick={onConfirmClick} onCancelClick={() => setIsOpen(false)} confirmationButtonText={confirmationButtonText} /> ), [isOpen] ); return { Dialog, onOpen, }; }
And here’s an example of the client code:
import React from "react"; import { useConfirmationDialog } from './useConfirmationDialog' function Client() { const { Dialog, onOpen } = useConfirmationDialog({ headerText: "Delete this record?", bodyText: "Are you sure you want delete this record? This cannot be undone.", confirmationButtonText: "Delete", onConfirmClick: handleDeleteConfirm, }); function handleDeleteConfirm() { //TODO: delete } const handleDeleteClick = () => { onOpen(); }; return ( <div> <Dialog /> <button onClick={handleDeleteClick} /> </div> ); } export default Client;
One thing to note here is that this implementation works fine as long as your confirmation modal doesn’t have any controlled input elements. If you do have controlled inputs, it’s best to create a separate component for your modal. That’s because you don’t want the content of the modal, including those inputs, to re-render each time the user types something.
Properly handling async actions in your application is trickier than it seems at first. There are multiple state variables that you need to keep track of while the task is running. You want to keep the user informed that the action is processing by displaying a spinner. Also, you need to handle the errors and provide useful feedback when they happen. So it pays off to have an established framework for dealing with async tasks in your React project. And that’s where you might find useAsync
useful. Here’s an implementation of the useAsync
hook:
export const useAsync = ({ asyncFunction }) => { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [result, setResult] = useState(null); const execute = useCallback( async (...params) => { try { setLoading(true); const response = await asyncFunction(...params); setResult(response); } catch (e) { setError(e); } setLoading(false); }, [asyncFunction] ); return { error, result, loading, execute }; };
The client code:
import React from "react"; export default function Client() { const { loading, result, error, execute } = useAsync({ asyncFunction: someAsyncTask, }); async function someAsyncTask() { // perform async task } const handleClick = () => { execute(); }; return ( <div> {loading && <p>loading</p>} {!loading && result && <p>{result}</p>} {!loading && error?.message && <p>{error?.message}</p>} <button onClick={handleClick} /> </div> ); }
The hook is not hard to write yourself and that’s what we often do. But it might make sense for you to use a more mature library implementation instead. Here’s a great option.
Form validation is another part of React applications that people often find tedious. With that said, there are plenty of great libraries to help with forms management in React. One great alternative is formik. However, each of those libraries has a learning curve. And that learning curve often makes it not worth using in smaller projects. Particularly if you have others working with you and they are not familiar with those libraries.
But it doesn’t mean we can’t have simple abstractions for some of the code we often use. One such piece of code that we like to abstract is error validation. Checking forms before submitting to API and displaying validation results to the user is a must-have for any web application. Here’s an implementation of a simple useTrackErrors
hook that can help with that:
import React, { useState } from "react"; import FormControl from "./FormControl"; import Input from "./Input"; import onSignup from "./SignupAPI"; export const useTrackErrors = () => { const [errors, setErrors] = useState({}); const setErrors = (errsArray) => { const newErrors = { ...errors }; errsArray.forEach(({ key, value }) => { newErrors[key] = value; }); setErrors(newErrors); }; const clearErrors = () => { setErrors({}); }; return { errors, setErrors, clearErrors }; };
And here’s the client implementation:
import React, { useState } from "react"; import FormControl from "./FormControl"; import Input from "./Input"; import onSignup from "./SignupAPI"; export default function Client() { const { errors, setErrors, clearErrors } = useTrackErrors(); const [name, setName] = useState(""); const [email, setEmail] = useState(""); const handleSignupClick = () => { let invalid = false; const errs = []; if (!name) { errs.push({ key: "name", value: true }); invalid = true; } if (!email) { errs.push({ key: "email", value: true }); invalid = true; } if (invalid) { setErrors(errs); return; } onSignup(name, email); clearErrors(); }; const handleNameChange = (e) => { setName(e.target.value); setErrors([{ key: "name", value: false }]); }; const handleEmailChange = (e) => { setEmail(e.target.value); setErrors([{ key: "email", value: false }]); }; return ( <div> <FormControl isInvalid={errors["name"]}> <FormLabel>Full Name</FormLabel> <Input onKeyDown={handleKeyDown} onChange={handleNameChange} value={name} placeholder="Your name..." /> </FormControl> <FormControl isInvalid={errors["email"]}> <FormLabel>Email</FormLabel> <Input onKeyDown={handleKeyDown} onChange={handleEmailChange} value={email} placeholder="Your email..." /> </FormControl> <button onClick={handleSignupClick}>Sign Up</button> </div> ); }
Debouncing has a broad use in any application. The most common use is throttling expensive operations. For example, preventing the application from calling the search API every time the user presses a key and letting the user finish before calling it. The useDebounce
hook makes throttling such expensive operations easy. Here’s a simple implementation that’s written using AwesomeDebounceLibrary under the hood:
import AwesomeDebouncePromise from "awesome-debounce-promise"; const debounceAction = (actionFunc, delay) => AwesomeDebouncePromise(actionFunc, delay); function useDebounce(func, delay) { const debouncedFunction = useMemo(() => debounceAction(func, delay), [ delay, func, ]); return debouncedFunction; }
And here’s the client code:
import React from "react"; const callAPI = async (value) => { // expensice API call }; export default function Client() { const debouncedAPICall = useDebounce(callAPI, 500); const handleInputChange = async (e) => { debouncedAPICall(e.target.value); }; return ( <form> <input type="text" onChange={handleInputChange} /> </form> ); }
One thing to note with this implementation: You need to ensure that the expensive function is not recreated on each render. Because that will reset the debounced version of that function and wipe out its inner state. There are two ways to achieve that:
And that’s it for this post. There are many useful hook libraries worth checking out, and if you’re interested, here’s a great place to start. While there are many helpful custom hooks out there, these five are the ones that you will find handy in any React project.
For more information and to develop web application using React JS, Hire React Developer from us as we give you a high-quality product by utilizing all the latest tools and advanced technology. E-mail us any clock at – hello@hkinfosoft.com or Skype us: “hkinfosoft”. To develop custom web apps using React JS, please visit our technology page.
Content Source:
57 Sherway St,
Stoney Creek, ON
L8J 0J3
606, Suvas Scala,
S P Ring Road, Nikol,
Ahmedabad 380049
1131 Baycrest Drive,
Wesley Chapel,
FL 33544
57 Sherway St,
Stoney Creek, ON
L8J 0J3
606, Suvas Scala,
S P Ring Road, Nikol,
Ahmedabad 380049
1131 Baycrest Drive,
Wesley Chapel,
FL 33544
© 2024 — HK Infosoft. All Rights Reserved.
© 2024 — HK Infosoft. All Rights Reserved.
T&C | Privacy Policy | Sitemap