DEV Community

Frulow
Frulow

Posted on

Asking For Confirmation Inline In React Function

Suppose a situation in React where you want to ask for confirmation from the user.

For example, you want to show them a custom popup asking if they really want to delete something or not. This is a
situation I am going to cover.

Let's for example, take a function onDeletePost. Which is called to delete a post. Hence, we are asking for a confirmation from the user, if they intend to do so.

In code we do so like:


const [showPopup, setShowPopup] = useState<boolean>(false)

const onDeletePost = async (id: string) => {
    try {

      if (!showPopup) {
       setShowPopup(true)
       return;
      }

      setShowPopup(false)

      await // call_api()

    } catch (error: any) {
      ...
    }
  };


return (
       <AskConfirmationPopup 
          show={showPopup} 
          onConfirmed={onDeletePost} 
          onCancel={() => setShowPopup(false)} 
       />
)

Enter fullscreen mode Exit fullscreen mode

image from google

You do not want to do that everywhere, in each and every component, so let's make it re-usable.

// confirmContext.tsx


import {
  PropsWithChildren,
  createContext,
  useCallback,
  useContext,
  useMemo,
  useState,
} from 'react';

import ConfirmBox, { PromptConfirmOptions, PromptConfirmProps } from '@components/UI/PromptConfirm';

export const ConfirmContext = createContext<{
  askConfirm: (
    title: PromptConfirmProps['title'],
    message: PromptConfirmProps['description'],
    options?: Partial<PromptConfirmOptions>
  ) => Promise<boolean>;
}>({
  askConfirm: async () => false,
});

export const ConfirmProvider = ({ children }: PropsWithChildren) => {
  const [open, setOpen] = useState(false);
  const [title, setTitle] = useState<PromptConfirmProps['title']>('');
  const [message, setMessage] = useState<PromptConfirmProps['description']>('');
  const [options, setOptions] = useState<Partial<PromptConfirmOptions>>({});
  const [resolve, setResolve] = useState<(value: boolean) => void>(() => {
    //anything
  });

  const confirm = useCallback(
    async (
      title: PromptConfirmProps['title'],
      message: PromptConfirmProps['description'],
      options?: Partial<PromptConfirmOptions>
    ) =>
      new Promise<boolean>((res) => {
        setOpen(true);
        setTitle(title);
        setMessage(message);
        setOptions(options ?? {});
        setResolve(() => res);
      }),
    []
  );

  const handleAction = (confirmed: boolean) => {
    setOpen(false);
    resolve(confirmed);
  };

  const values = useMemo(() => {
    return { askConfirm: confirm };
  }, [confirm]);

  return (
    <ConfirmContext.Provider value={values}>
      {children}
      {open && (
        <ConfirmBox title={title} description={message} onAction={handleAction} options={options} />
      )}
    </ConfirmContext.Provider>
  );
};

export const useConfirmBox = () => useContext(ConfirmContext);

Enter fullscreen mode Exit fullscreen mode

I am using @mui/material (basically material ui component library

// PromptConfirm.tsx - The main Popup Component To show

import * as React from 'react';

import { ThemeProvider, Typography } from '@mui/material';
import Button, { ButtonProps } from '@mui/material/Button';
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogContentText, { DialogContentTextProps } from '@mui/material/DialogContentText';
import DialogTitle from '@mui/material/DialogTitle';

import lightTheme from '@styles/theme/lightTheme';

export interface PromptConfirmOptions {
  acceptButtonProps: ButtonProps;
  rejectButtonProps: ButtonProps;
  descriptionProps: DialogContentTextProps;
}
export interface PromptConfirmProps {
  title: React.ReactElement | string;
  description: React.ReactElement | string;
  options: Partial<PromptConfirmOptions>;
  onAction: (proceed: boolean) => void;
}

function ConfirmBox({ title, description, onAction, options }: PromptConfirmProps) {
  const _handleCancel = () => onAction(false);
  const _handleOkay = () => onAction(true);

  return (
    <ThemeProvider theme={lightTheme}>
      <Dialog
        open
        onClose={_handleCancel}
        aria-labelledby='alert-dialog-title'
        aria-describedby='alert-dialog-description'
        PaperProps={{ sx: { borderRadius: 3, p: 1, maxWidth: '20em' } }}
      >
        <DialogTitle id='alert-dialog-title'>
          <Typography variant='h5' sx={{ fontWeight: 700 }}>
            {title}
          </Typography>
        </DialogTitle>
        <DialogContent>
          <DialogContentText id='alert-dialog-description' {...options?.descriptionProps}>
            {description}
          </DialogContentText>
        </DialogContent>
        <DialogActions sx={{ px: 3 }}>
          <Button
            variant='contained'
            fullWidth
            color='error'
            {...options?.acceptButtonProps}
            onClick={_handleOkay}
          >
            Yes
          </Button>
          <Button
            variant='contained'
            autoFocus
            color='primary'
            fullWidth
            {...options?.rejectButtonProps}
            onClick={_handleCancel}
          >
            No
          </Button>
        </DialogActions>
      </Dialog>
    </ThemeProvider>
  );
}

export default ConfirmBox;

Enter fullscreen mode Exit fullscreen mode

Wrap your root component or App.tsx or Index.tsx with below:


import { ConfirmProvider } from 'path/to/confirmContext.tsx';

export default function App(){
return ( <ConfirmProvider> ... </ConfirmProvider> )
}

Enter fullscreen mode Exit fullscreen mode

And now use it anywhere like below


  const { askConfirm } = useConfirmBox();


 const onDeletePost = async (id: string) => {
    try {
      const confirmed = await askConfirm(
        [title], // Delete now?
        [description], // Are you sure?
      );
      if (!confirmed) return; // return if not confirmed
      ...
      // do your thing here

    }catch(erro)...

Enter fullscreen mode Exit fullscreen mode

And here you have a re-usable hooks to call inline in a function from anywhere

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)