Skip to content

Instantly share code, notes, and snippets.

@sinclairnick
Last active April 28, 2022 00:46
Show Gist options
  • Save sinclairnick/81e7a316b6a68c45ca7bbe539b19e333 to your computer and use it in GitHub Desktop.
Save sinclairnick/81e7a316b6a68c45ca7bbe539b19e333 to your computer and use it in GitHub Desktop.
Await-able custom confirmation dialog in React, i.e. a custom window.confirm
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
} from "@mui/material";
import { createContext, FC, useContext, useRef, useState } from "react";
import { defaultConfirmArgs } from "./confirmation.constants";
import {
ConfirmationContextType,
ConfirmationProps,
ConfirmFn,
} from "./confirmation.types";
const ConfirmationContext = createContext<ConfirmationContextType>({} as any);
export const ConfirmationProvider: FC<ConfirmationProps> = (props) => {
const [isDialogOpen, setIsDialogOpen] = useState(false);
const confirmArgsRef = useRef(defaultConfirmArgs);
const handleYesRef = useRef(() => {});
const handleNoRef = useRef(() => {});
const handleClose = () => {
handleNoRef.current();
setIsDialogOpen(false);
};
const handleConfirm = () => {
handleYesRef.current();
setIsDialogOpen(false);
};
const confirm: ConfirmFn = async (args = defaultConfirmArgs) => {
confirmArgsRef.current = args;
setIsDialogOpen(true);
return new Promise((res, rej) => {
handleYesRef.current = () => res(true);
handleNoRef.current = () => res(false);
});
};
return (
<>
<ConfirmationContext.Provider value={{ confirm }}>
{props.children}
</ConfirmationContext.Provider>
<Dialog open={isDialogOpen} onClose={handleClose}>
<DialogTitle>{confirmArgsRef.current.title}</DialogTitle>
<DialogContent>
<DialogContentText>
{confirmArgsRef.current.description}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button color="inherit" onClick={handleClose}>
Cancel
</Button>
<Button color="primary" variant="contained" onClick={handleConfirm}>
Confirm
</Button>
</DialogActions>
</Dialog>
</>
);
};
export const useConfirm = () => {
const ctx = useContext(ConfirmationContext);
if (ctx == null) {
throw new Error("Cannot use confirmation context outside provider");
}
return ctx.confirm;
};
import { ConfirmArgs } from "./confirmation.types";
export const defaultConfirmArgs: ConfirmArgs = {
title: "Are you sure?",
};
import { ReactNode } from "react";
export type ConfirmationProps = {};
export type ConfirmArgs = {
title?: ReactNode;
description?: ReactNode;
};
export type ConfirmFn = (args?: ConfirmArgs) => Promise<boolean>;
export type ConfirmationContextType = {
confirm: ConfirmFn;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment