Implementando Modal de Confirmação Reutilizável com React
Vitor Rios
Posted on June 27, 2024
Introdução
Modais de confirmação são componentes essenciais em muitas aplicações web, permitindo que os usuários confirmem ações críticas antes de prosseguir. Neste artigo, apresento uma solução prática para criar um modal de confirmação reutilizável em React. A abordagem utiliza um contexto para gerenciar o estado do modal e permite personalizar facilmente o título, subtítulo e textos dos botões. Esta solução foi desenvolvida com base em várias fontes e aprimorada para oferecer um fluxo de confirmação eficiente e consistente.
Estrutura do Projeto
A estrutura básica do projeto inclui os seguintes arquivos:
confirmation-modal.tsx
confirmation-modal.context.tsx
use-confirmation-modal.ts
types.ts
App.tsx
index.tsx
Componente de Modal de Confirmação
O componente ConfirmationModal
é responsável por renderizar o modal com base nas propriedades recebidas. Utiliza o Modal
do Ant Design para a estrutura do modal.
import { Modal } from "antd";
import React, { useEffect } from "react";
import { useConfirmationModal } from "./use-confirmation-modal";
interface IConfirmationModalProps {
children?: React.ReactNode;
}
const ConfirmationModal = ({ children }: IConfirmationModalProps) => {
const { isOpen, confirmationArgs, actions } = useConfirmationModal();
const handleCancel = () => actions.cancel?.();
const handleConfirm = () => actions.proceed?.();
useEffect(() => {
if (!actions.proceed) return;
const handleKeydown = (e: any) => {
if (actions.proceed && isOpen && e.key === "Enter") {
actions.proceed();
}
};
window.addEventListener("keydown", handleKeydown);
return () => window.removeEventListener("keydown", handleKeydown);
}, [actions.proceed, isOpen]);
return (
<Modal
title={confirmationArgs.title}
okText={confirmationArgs.confirmActionText || "Confirm"}
cancelText={confirmationArgs.cancelActionText || "Cancel"}
onCancel={handleCancel}
onOk={handleConfirm}
open={isOpen}
width={640}
>
<div>
<p>{confirmationArgs.subtitle}</p>
{children}
</div>
</Modal>
);
};
export default ConfirmationModal;
Explicação Detalhada do Componente
-
Importações: Importamos
Modal
do Ant Design euseEffect
do React para gerenciar o ciclo de vida do componente. -
Props: Definimos
IConfirmationModalProps
para aceitar children, permitindo flexibilidade na renderização do conteúdo do modal. -
Hook Personalizado: Utilizamos
useConfirmationModal
para acessar o estado e as ações do contexto de confirmação. -
Funções de Manipulação:
handleCancel
ehandleConfirm
chamam as funções de cancelamento e confirmação armazenadas no contexto. -
Efeito Colateral: Usamos
useEffect
para adicionar e remover um ouvinte de eventos de teclado que aciona a confirmação ao pressionar a tecla Enter. -
Renderização do Modal: Utilizamos o componente
Modal
do Ant Design para exibir o modal, personalizando o título, texto dos botões e conteúdo com base nos argumentos de confirmação.
Contexto para Gerenciamento de Estado
O contexto ConfirmationModalContext
gerencia o estado do modal, incluindo se está aberto, os argumentos de confirmação e as ações de confirmar e cancelar.
import { createContext, ReactNode, useState } from "react";
import ConfirmationModal from "./confirmation-modal";
import { IActions, IConfirmationArgs, IContextType } from "./types";
const INITIAL_CONFIRMATION_STATE: IConfirmationArgs = {
title: "",
subtitle: "",
confirmActionText: "",
cancelActionText: "",
};
export const ConfirmationModalContext = createContext<IContextType>({
actions: {
proceed: null,
cancel: null,
},
confirmationArgs: INITIAL_CONFIRMATION_STATE,
isOpen: false,
isConfirmed: () => Promise.resolve(true),
});
interface IProviderProps {
children: ReactNode;
}
export function ConfirmationModalProvider({ children }: IProviderProps) {
const [actions, setActions] = useState<IActions>({
proceed: null,
cancel: null,
});
const [isOpen, setIsOpen] = useState(false);
const [confirmationArgs, setConfirmationArgs] = useState<IConfirmationArgs>(
INITIAL_CONFIRMATION_STATE
);
const isConfirmed = (confirmationArgsData: Partial<IConfirmationArgs>) => {
const promise = new Promise((resolve, reject) => {
setActions({ proceed: resolve, cancel: reject });
setConfirmationArgs({
...INITIAL_CONFIRMATION_STATE,
...confirmationArgsData,
});
setIsOpen(true);
});
return promise.then(
(): boolean => {
setIsOpen(false);
return true;
},
(): boolean => {
setIsOpen(false);
return false;
}
);
};
return (
<ConfirmationModalContext.Provider
value={{ isOpen, isConfirmed, confirmationArgs, actions }}
>
<ConfirmationModal />
{children}
</ConfirmationModalContext.Provider>
);
}
Explicação Detalhada do Contexto
-
Estado Inicial:
INITIAL_CONFIRMATION_STATE
define o estado inicial das propriedades do modal de confirmação. -
Criação do Contexto:
ConfirmationModalContext
cria o contexto que armazenará o estado e as ações do modal. -
Provider:
ConfirmationModalProvider
é o componente que encapsula a lógica de estado do modal e fornece o contexto para seus filhos. -
Função
isConfirmed
: Esta função aceita argumentos de confirmação, atualiza o estado do modal e retorna uma promessa que resolve ou rejeita com base na ação do usuário.
Hook para Acesso ao Contexto
O hook useConfirmationModal
facilita o acesso ao contexto dentro de outros componentes.
import React from "react";
import { ConfirmationModalContext } from "./confirmation-modal.context";
export function useConfirmationModal() {
const { isOpen, isConfirmed, confirmationArgs, actions } = React.useContext(
ConfirmationModalContext
);
return { isOpen, isConfirmed, confirmationArgs, actions };
}
Explicação Detalhada do Hook
-
Importações: Importamos
React
eConfirmationModalContext
. -
Uso do Contexto:
useConfirmationModal
utilizaReact.useContext
para acessar e retornar o estado e as ações do contexto.
Definição de Tipos
Definimos os tipos necessários para as propriedades e estado do modal em types.ts
.
import { ButtonProps } from "antd";
export interface IActions {
proceed: null | ((value?: unknown) => void);
cancel: null | ((value?: unknown) => void);
}
export interface IConfirmationArgs {
title: string;
subtitle: string;
confirmActionText?: string;
cancelActionText?: string;
customCancelAction?: () => void;
confirmButtonCustomType?: ButtonProps["type"];
cancelButtonCustomType?: ButtonProps["type"];
}
export interface IContextType {
actions: IActions;
confirmationArgs: IConfirmationArgs;
isOpen: boolean;
isConfirmed: (
confirmationArgsData: Partial<IConfirmationArgs>
) => Promise<boolean>;
}
Explicação Detalhada dos Tipos
- IActions: Define as funções de ação de proceder e cancelar.
- IConfirmationArgs: Define os argumentos de configuração do modal, incluindo título, subtítulo e textos dos botões.
-
IContextType: Define o formato do contexto, incluindo ações, argumentos de confirmação, estado de visibilidade e função
isConfirmed
.
Componente Principal
O componente App
demonstra como usar o modal de confirmação para ações como deletar um item ou resetar uma lista.
import { Button } from "antd";
import { useState } from "react";
import { useConfirmationModal } from "./components/confirmation-modal";
import "./styles.css";
const INITIAL_STATE = ["banana", "watermelon", "grape", "orange"];
export default function App() {
const [list, setList] = useState(INITIAL_STATE);
const { isConfirmed } = useConfirmationModal();
const handleDelete = async (fruit: string) => {
const willDelete = await isConfirmed({
title: "Delete Fruit",
subtitle: `Are you sure you want to delete the ${fruit}?`,
confirmActionText: `Delete ${fruit}`,
});
if (!willDelete) return;
setList((prev) => prev.filter((item) => item !== fruit));
};
const reset = async () => {
const willReset = await isConfirmed({
title: "Reset",
subtitle: `Are you sure you want to reset the list?`,
confirmActionText: `Reset List`,
});
if (!willReset) return;
setList(INITIAL_STATE);
};
return (
<div className="App">
<h1>Confirmation Modal</h1>
<h2>POC to confirmation modal flow!</
h2>
<Button type="primary" onClick={reset}>
Reset
</Button>
<ul>
{list.map((fruit) => {
return (
<li key={fruit}>
<Button type="default" onClick={() => handleDelete(fruit)}>
X
</Button>{" "}
{fruit}
</li>
);
})}
</ul>
</div>
);
}
Explicação Detalhada do Componente Principal
-
Estado Inicial:
INITIAL_STATE
define a lista inicial de itens. -
Estado da Lista: Utilizamos
useState
para gerenciar a lista de itens. -
Uso do Hook: Utilizamos
useConfirmationModal
para acessar a funçãoisConfirmed
. -
Função
handleDelete
: Mostra o modal de confirmação e remove o item da lista se a confirmação for positiva. -
Função
reset
: Mostra o modal de confirmação e reseta a lista se a confirmação for positiva. - Renderização: Renderiza um botão para resetar a lista e uma lista de itens com botões para deletar cada item.
Ponto de Entrada da Aplicação
Por fim, configuramos o ponto de entrada da aplicação para usar o ConfirmationModalProvider
.
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { ConfirmationModalProvider } from "./components/confirmation-modal";
const rootElement = document.getElementById("root")!;
const root = ReactDOM.createRoot(rootElement);
root.render(
<React.StrictMode>
<ConfirmationModalProvider>
<App />
</ConfirmationModalProvider>
</React.StrictMode>
);
Explicação Detalhada do Ponto de Entrada
-
Importações: Importamos
React
,ReactDOM
,App
eConfirmationModalProvider
. - Configuração do Root: Criamos o root element e configuramos o renderizador do React.
-
Renderização: Envolvemos o
App
comConfirmationModalProvider
para fornecer o contexto a toda a aplicação.
Benefícios desta Abordagem
- Reutilização: O modal de confirmação é reutilizável em toda a aplicação, evitando duplicação de código.
- Flexibilidade: É fácil personalizar o conteúdo e o comportamento do modal para diferentes cenários.
- Simplicidade: A lógica de exibição e personalização do modal é centralizada, tornando o código mais fácil de manter.
- Consistência: Garante uma experiência de usuário consistente ao usar o mesmo modal para diferentes ações de confirmação.
Conclusão
Modais de confirmação são componentes importantes para a interação do usuário com ações críticas. Implementar um modal de confirmação reutilizável e personalizável, como mostrado neste artigo, melhora a consistência e a eficiência da aplicação. Esta solução aproveita o poder dos hooks do React e o contexto para gerenciar o estado do modal, oferecendo uma abordagem flexível e escalável para confirmar ações do usuário.
Espero que esta implementação ajude você a adicionar modais de confirmação eficazes em suas aplicações React. Sinta-se à vontade para adaptar e expandir esta solução conforme necessário para atender às suas necessidades específicas.
Você pode conferir o código completo e funcional no CodeSandbox através do link.
Posted on June 27, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.