Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
August 28, 2022 01:49 pm GMT

Using a Custom Hook to Control a Popup

Intro

The ability to reuse code is a great thing. I have encountered a situation where I have to build four different types of popups for an application.

Instead of writing four separate components, a single hybrid component would cater for the need.

The global popup I created makes use of some state checks to render individual UI elements. I used recoil for state management such that the various states of the component can be controlled globally. Wrapping up the control aspect into a hook will make it easy to use from various parts of the application.

In this post, I used Next.js for the demonstration.

Folder Structure

folder structure

States

Each unit of state(atom) is stored in separate files and these are served from the index.js file of src/recoil/popup.

There are states included to

  • open or close the popup,
  • add a heading,
  • enable or disable the close button,
  • display content with a custom component,
  • enable or disable images, and
  • pass event handlers to buttons.

Also, it is given a provision to reset the state of the popup to its default value.

src/recoil/popup/index.js

import popupOpenOrClose from './popupOpenOrClose'import popupHeading from './popupHeading'import popupContentCloseMark from './popupContentCloseMark'import popupCustomContent from './popupCustomContent'import popupThumbsUp from './popupThumbsUp'import popupThumbDown from './popupThumbDown'import popupOkButton from './popupOkButton'import popupCancelButton from './popupCancelButton'import popupYesButton from './popupYesButton'import useResetPopupState from './resetStates'export {    popupOpenOrClose,    popupHeading,    popupContentCloseMark,    popupCustomContent,    popupThumbsUp,    popupThumbDown,    popupOkButton,    popupCancelButton,    popupYesButton,    useResetPopupState}

src/recoil/popup/popupCancelButton.js

import { atom } from "recoil"const popupCancelButton = atom({    key: "popupCancelButton",    default: false})export default popupCancelButton

The popupCancelButton atom store the function that must be executed on the onClick event of the cancel button.

src/recoil/popup/popupContentCloseMark.js

import { atom } from "recoil"const popupContentCloseMark = atom({    key: "popupContentCloseMark",    default: false})export default popupContentCloseMark

The popupContentCloseMark atom store the state of whether the close button must be present or not in the popup.

src/recoil/popup/popupCustomContent.js

import { atom } from "recoil"const popupCustomContent = atom({    key: "popupCustomContent",    default: <></>})export default popupCustomContent

The popupCustomContent atom store the custom content component that must be present or not in the popup.

src/recoil/popup/popupHeading.js

import { atom } from "recoil"const popupHeading = atom({    key: "popupHeading",    default: ''})export default popupHeading

The popupHeading atom store the heading that must be present or not in the popup.

src/recoil/popup/popupOkButton.js

import { atom } from "recoil"const popupOkButton = atom({    key: "popupOkButton",    default: false})export default popupOkButton

The popupOkButton atom store the function that must be executed on the onClick event of the Ok button.

src/recoil/popup/popupOpenOrClose.js

import { atom } from "recoil"const popupOpenOrClose = atom({    key: "popupOpenOrClose",    default: false})export default popupOpenOrClose

The popupOpenOrClose atom store the state of whether the popup must be present or not.

src/recoil/popup/popupThumbDown.js

import { atom } from "recoil"const popupThumbDown = atom({    key: "popupThumbDown",    default: false})export default popupThumbDown

The popupThumbDown atom store the state of whether the thumb down image must be present or not in the popup.

src/recoil/popup/popupThumbsUp.js

import { atom } from "recoil"const popupThumbsUp = atom({    key: "popupThumbsUp",    default: false})export default popupThumbsUp

The popupThumbsUp atom store the state of whether the thumb up image must be present or not in the popup.

src/recoil/popup/popupYesButton.js

import { atom } from "recoil"const popupYesButton = atom({    key: "popupYesButton",    default: false})export default popupYesButton

The popupYesButton atom store the function that must be executed on the onClick event of the Yes button.

src/recoil/popup/resetStates.js

import { useResetRecoilState } from "recoil";import {    popupOpenOrClose,    popupHeading,    popupContentCloseMark,    popupCustomContent,    popupThumbsUp,    popupThumbDown,    popupOkButton,    popupCancelButton,    popupYesButton,} from '../../recoil/popup'const useResetPopupState = () => {    // reset state references    const resetOpenOrClosePopup = useResetRecoilState(popupOpenOrClose);    const resetPopupHeading = useResetRecoilState(popupHeading);    const resetContentCloseMarkPopup = useResetRecoilState(popupContentCloseMark);    const resetCustomContentPopup = useResetRecoilState(popupCustomContent);    const resetThumbsUpPopup = useResetRecoilState(popupThumbsUp);    const resetThumbDownPopup = useResetRecoilState(popupThumbDown);    const resetOkButtonPopup = useResetRecoilState(popupOkButton);    const resetCancelButtonPopup = useResetRecoilState(popupCancelButton);    const resetYesButtonPopup = useResetRecoilState(popupYesButton);    const resetState=()=>{        //reset to default    resetOpenOrClosePopup()    resetPopupHeading()    resetContentCloseMarkPopup()    resetCustomContentPopup()    resetThumbsUpPopup()    resetThumbDownPopup()    resetOkButtonPopup()    resetCancelButtonPopup()    resetYesButtonPopup()    }    return [resetState]}export default useResetPopupState

The useResetPopupState is a hook used to reset the states related to the popup. It returns resetState, by invoking resetState we can reset the popup states to their default values.

Custom Hook

src/hooks/usePopUp.js

import { useSetRecoilState } from "recoil";import {    popupOpenOrClose,    popupHeading,    popupContentCloseMark,    popupCustomContent,    popupThumbsUp,    popupThumbDown,    popupOkButton,    popupCancelButton,    popupYesButton,    useResetPopupState} from '../recoil/popup'export const usePopUp = () => {    //states    const setOpenOrClosePopup = useSetRecoilState(popupOpenOrClose);    const setPopupHeading = useSetRecoilState(popupHeading);    const setContentCloseMarkPopup = useSetRecoilState(popupContentCloseMark);    const setcustomContentPopup = useSetRecoilState(popupCustomContent);    const setThumbsUpPopup = useSetRecoilState(popupThumbsUp);    const setThumbDownPopup = useSetRecoilState(popupThumbDown);    const setOkButtonPopup = useSetRecoilState(popupOkButton);    const setCancelButtonPopup = useSetRecoilState(popupCancelButton);    const setYesButtonPopup = useSetRecoilState(popupYesButton);    // hook    const [resetPopUp] = useResetPopupState()    // showPopup implmentation    const showPopup = (        {            heading,            showClose,            customContent,            ThumbUp,            ThumbDown,            onOkPressed,            onCancelpressed,            onYesPressed,        }) => {        resetPopUp()        setOpenOrClosePopup(true)        setPopupHeading(heading)        setContentCloseMarkPopup(showClose)        setcustomContentPopup(customContent)        setThumbsUpPopup(ThumbUp)        setThumbDownPopup(ThumbDown)        if (onOkPressed) {            setOkButtonPopup({ onClick: onOkPressed })        }        if (onCancelpressed) {            setCancelButtonPopup({ onClick: onCancelpressed })        }        if (onYesPressed) {            setYesButtonPopup({ onClick: onYesPressed })        }    }    // hidePopup implmentation    const hidePopup = () => {        resetPopUp()    }    return [showPopup, hidePopup]}

The usePopUp() hook return [showPopup,hidePopup].

  • The showPopup() function takes an object as an argument which is used to set the properties as well as various event handlers for the popup.
  • The hidePopup() function hides the popup and sets its states to default value.

Popup Component

src/components/globalPopup/index.js

import React, { useEffect } from 'react'import { useRecoilState, useRecoilValue } from "recoil";import {    popupOpenOrClose,    popupHeading,    popupContentCloseMark,    popupCustomContent,    popupThumbsUp,    popupThumbDown,    popupOkButton,    popupCancelButton,    popupYesButton,    useResetPopupState} from '../../recoil/popup'import closeButton from '../../images/closeButton.png'import thumbsUpImg from '../../images/thumbsUp.jpeg'import innerClose from '../../images/innerClose.png'import thumbsDownImg from '../../images/thumbsDown.jpeg'import Image from 'next/image'import styles from './globalPopup.module.css'const GlobalPopup = () => {    // states    const [openOrClosePopup, setOpenOrClosePopup] = useRecoilState(popupOpenOrClose);    // values    const headingPopup = useRecoilValue(popupHeading);    const contentCloseMarkPopup = useRecoilValue(popupContentCloseMark);    const customContent = useRecoilValue(popupCustomContent);    const thumbsUpPopup = useRecoilValue(popupThumbsUp);    const thumbDownPopup = useRecoilValue(popupThumbDown);    const okButtonPopup = useRecoilValue(popupOkButton);    const cancelButtonPopup = useRecoilValue(popupCancelButton);    const yesButtonPopup = useRecoilValue(popupYesButton);    // hook    const[resetPopUp]=useResetPopupState()    // handling background scroll of body    useEffect(() => {        document.body.style.overflow = 'hidden'        return () => {            document.body.style.overflow = 'visible'            //reset to default values            resetPopUp()        }    }, [])    //event handlers    const toggleOpenOrClosePopup = () => {        setOpenOrClosePopup((prev) => { return !prev })    }    return (        openOrClosePopup && (            <div className={styles.popupWrap}>                <div className={styles.closeButtonWrap}>                    {contentCloseMarkPopup && (                        <div                            className={styles.closeButton}                            onClick={toggleOpenOrClosePopup}                        >                            <Image src={closeButton} layout="fill" />                        </div>                    )}                </div>                <div className={styles.boxBig}>                    <div className={styles.boxSmall}>                        {contentCloseMarkPopup && (                            < div className={styles.innerCloseWrap} onClick={toggleOpenOrClosePopup}>                                <Image src={innerClose} width="20" height="20" />                            </div>                        )}                        {thumbsUpPopup && (                            <div className={styles.popUpMainImage}>                                <Image src={thumbsUpImg} layout="fill" />                            </div>                        )}                        {thumbDownPopup && (                            <div className={styles.popUpMainImage}>                                <Image src={thumbsDownImg} layout="fill" />                            </div>                        )}                        <div className={styles.newHeading}>                            <div className={styles.newHeadingInner}>                                <h1> {headingPopup}                                </h1>                            </div>                        </div>                        <p className={styles.prompt}>                            {customContent}                        </p>                        {(okButtonPopup || yesButtonPopup || cancelButtonPopup) && (                            <div className={styles.actionButtions}>                                {okButtonPopup && <div className={styles.actionButtionWrap} onClick={okButtonPopup.onClick}>                                    <button className={styles.buttonSmall} >Ok</button>                                </div>                                }                                {yesButtonPopup && <div className={styles.actionButtionWrap} onClick={yesButtonPopup.onClick}>                                    <button className={styles.buttonSmall} >Yes</button>                                </div>}                                {cancelButtonPopup && <div className={styles.actionButtionWrap} onClick={cancelButtonPopup.onClick}>                                    <button className={styles.buttonSmall} >Cancel</button>                                </div>}                            </div>                        )}                    </div>                </div>            </div >)    )}export default GlobalPopup

The GlobalPopup makes use of the states that are available globally to render UI. The code inside the useEffect block prevents the scroll of the body when the popup is active and makes overflow visible when the popup is inactive.

Popup CSS

src/components/globalPopup/globalPopup.module.css

.popupWrap {  position: fixed;  height: 100vh;  width: 100vw;  background-color: rgba(0, 0, 0, 0.1);  background-size: cover;  top: 0;  left: 0;  z-index: 99;}.closeButtonWrap {  position: absolute;  top: 10%;  right: 10%;  display: flex;  justify-content: flex-end;  cursor: pointer;}.closeButton {  position: relative;  width: 30px;  height: 30px;}.boxBig {  position: absolute;  top: 50%;  left: 50%;  transform: translate(-50%, -50%);}.boxSmall {  background: #fff;  border-radius: 16px;  border-top: 6px solid #5ddfb6;  padding: 40px 65px;  display: flex;  flex-direction: column;  align-items: center;}.actionButtions {  padding: 15px 0px;  display: flex;  flex-direction: row;  justify-content: space-around;}.actionButtionWrap {  margin: 0px 5px;}.actionButtionWrap button {  cursor: pointer;}.popUpMainImage {  position: relative;  width: 100px;  aspect-ratio: 1/1;}.specificEmail {  color: #9e8959;}.newHeading {  display: flex;  flex-direction: column;  width: 100%;  justify-content: center;  align-items: center;}.newHeadingInner {  display: flex;  flex-direction: column;  justify-content: center;  align-items: center;  padding-top: 10px;}.newHeading h1 {  margin-bottom: 10px;  font-size: 30px;  font-weight: 500;  color: #000000;  text-align: center;}.prompt {  font-size: 16px;  line-height: 150%;  margin-top: 10px;  text-align: center;  font-weight: 400;  color: rgba(0, 0, 0, 0.68);}.prompt span {  color: #1cd7b8;}.innerCloseWrap {  display: none;}.buttonSmall {  width: 74px;  height: 27px;  font-family: "Poppins";  font-size: 0.8rem;  line-height: 24px;  background: #58c0ce;  border-radius: 3px;  border-color: #525251;  box-shadow: 0 0 0 0;  color: white;  border: none;  padding: 0;  padding: 2px 8px 2px 8px;  transition: 0.4s;}.buttonSmall:hover {  background: #5ddfb6;}@media screen and (max-width: 560px) {  .prompt {    font-size: 14px;    margin-top: 10px;  }  .closeButtonWrap {    display: none;  }  .boxBig {    width: 80%;  }  .boxSmall {    width: 100%;  }  .innerCloseWrap {    display: block;    position: absolute;    width: 30px;    right: 0px;    top: 15px;  }  .popUpMainImage {    position: relative;    width: 70px;    aspect-ratio: 1/1;  }  .newHeading h1 {    margin-bottom: 5px;    font-size: 25px;    font-weight: 500;    color: #000000;  }  .prompt {    margin-top: 10px;  }}@media screen and (max-width: 425px) {  .boxSmall {    padding: 40px 50px;  }}

This is the CSS used for the popup. The popup is mobile responsive also.

Include Component

src/pages/_app.js

import { RecoilRoot } from 'recoil'import GlobalPopup from '../components/globalPopup'import '../styles/globals.css'function MyApp({ Component, pageProps }) {    return (        <RecoilRoot>            <>                <Component {...pageProps} />                <GlobalPopup/>            </>        </RecoilRoot>)}export default MyApp

To make the popup available as a global entity in the application. It is included as a component in _app.js. The component is wrapped with the context provided by RecoilRoot to ensure the availability of atom states.

Test the Popup

src/pages/testPopUps/index.js

import React from 'react'import { usePopUp } from '../../hooks/usePopUp';const TestPopup = () => {    const [showPopup, hidePopup] = usePopUp()    // showPopup - use it for diplaying popups with specified options    // hidePopup - use it for hiding the popup (as well as to reset to default state)    return (        <div>            <button onClick={() => {                showPopup({                    ThumbUp: true,                    heading: 'Success !',                    showClose: true,                    customContent: <>The process is <span>Successful</span></>                })            }}>                Activate Popup 1            </button>            <button onClick={() => {                showPopup({                    showClose: true,                    ThumbDown: true,                    heading: 'Error !',                    customContent: <>An Error Occured.</>                })            }}>                Activate Popup 2            </button>            <button onClick={() => {                showPopup({                    customContent: <>An account with email <span>[email protected]</span> already exist</>,                    onCancelpressed: () => {                        console.log("onCancelpressed");                        hidePopup()                    },                    onYesPressed: () => {                        console.log("onYesPressed");                        hidePopup()                    }                })            }}>                Activate Popup 3            </button>            <button onClick={() => {                showPopup({                    customContent: <>An account with email <span>[email protected]</span> already exist</>,                    onOkPressed: () => {                        console.log("onOkPressed");                        hidePopup()                    }                })            }}>                Activate Popup 4            </button>        </div >    )}export default TestPopup

For testing the popup we created a page, in that page, there are four buttons and each of these buttons presents the popup in a different specification.

Using the showPopup() we can configure the popup with various options.

Output

End result

In this way, the same component is made to be reused, rendered conditionally and controlled from anywhere in the application.

Thank you.


Original Link: https://dev.to/bijishjs/control-popup-using-custom-hook-13io

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To