Lidando com Eventos
Outro caso comum, e que você já deve ter visto muitas vezes, é a lógica de lidar com eventos dentro do próprio componente.
Apesar do código estar simples, a responsabilidade do código pode ser divida em partes.
O código de escutar o escape pode ser extraído, pois também seria útil para menus, selects, ou qualquer outro componente que precise ouvir eventos de teclado.
import { useEffect, useState } from "react";
export function Modal({ isOpen, onChangeIsOpen }) {
useEffect(() => {
function handleClick(event: KeyboardEvent) {
if (event.key === "Escape") {
onChangeIsOpen(false);
}
}
window.addEventListener("keydown", handleClick);
return () => {
window.removeEventListener("keydown", handleClick);
};
}, []);
return (
<div>
<dialog open={isOpen}>
<p>Greetings, one and all!</p>
<button onClick={() => onChangeIsOpen(false)}>OK</button>
</dialog>
<button onClick={() => onChangeIsOpen(true)}>Abrir Modal</button>
</div>
);
}
Extraindo a lógica de escutar o teclado em um hook
No hook, a ideia é que você possa passar o código da tecla e a função de
callback, e o hook vai lidar com a lógica de escutar e remover o evento de teclado.
// useKeyPress.tsx
import { useEffect } from "react";
type useKeyPressParams = {
keyCode: string;
callback: (event: KeyboardEvent) => void;
};
export function useKeyPress({ keyCode, callback }: useKeyPressParams) {
useEffect(() => {
function handleClick(event: KeyboardEvent) {
if (event.key === keyCode) {
callback(event);
}
}
window.addEventListener("keydown", handleClick);
return () => {
window.removeEventListener("keydown", handleClick);
};
}, [keyCode, callback]);
}
Assim, o modal tem menos responsabilidades e o hook pode ser reutilizado em outros componentes.
// Modal.tsx
import { useCallback, useState } from "react";
import { useKeyPress } from "../hooks/useKeyPress";
export function Modal({isOpen, onChangeIsOpen}) {
useKeyPress({ keyCode: "Escape", callback: onChangeIsOpen });
return (
<div>
<dialog open={isOpen}>
<p>Greetings, one and all!</p>
<button onClick={() => onChangeIsOpen(false)}>OK</button>
</dialog>
<button onClick={() => onChangeIsOpen(true)}>Abrir Modal</button>
</div>
);
}
Isolando a lógica de abrir e fechar o modal em um hook
No exemplo acima, estamos delegando ao componente PAI para lidar com o estado do modal,
ainda, sim, dentro do próprio pai um estado deve ser criado e gerenciado.
É possível também encapsular a lógica de abrir e fechar o modal em um hook customizado,
assim o componente que usa o modal não precisa lidar com o estado do modal.
O código não está mais ou menos complexo, mas a responsabilidade de manter o estado
aberto ou fechado do modal foi delegada para o próprio hook.
// hooks/useModal.tsx
import { PropsWithChildren, useCallback, useState } from "react";
export function useModal() {
const [isOpen, setIsOpen] = useState(false);
const closeModal = useCallback(() => {
setIsOpen(false);
}, []);
const openModal = useCallback(() => {
setIsOpen(true);
}, []);
const Modal = useCallback(
({ children }: PropsWithChildren) => (
<dialog open={isOpen}>{children}</dialog>
),
[isOpen],
);
return {
openModal,
closeModal,
Modal,
};
}
// App.tsx
import { useModal } from "./hooks/useModal";
function App() {
const { Modal, openModal, closeModal } = useModal();
return (
<div>
<Modal>
<p>Greetings, one and all!</p>
<button onClick={closeModal}>OK</button>
</Modal>
<button onClick={openModal}></button>
</div>
);
}
export default App;
Top comments (0)