DEV Community

Cover image for React na Prática: Lidando com Eventos
Alvaro Guimarães
Alvaro Guimarães

Posted on

React na Prática: Lidando com Eventos

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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]);
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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,
  };
}
Enter fullscreen mode Exit fullscreen mode
// 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;
Enter fullscreen mode Exit fullscreen mode

Top comments (0)