Przejdź do treści głównej
  • Strona główna
  • Blog
  • Kontakt

Jak otypować zdarzenie onSubmit w formularzach korzystających z Reacta

Data: 2021-11-20 | Autor: Admin | Kategoria: TypeScript

Świecąca żarówka nad ręką w ciemności

Mając zwykły formularz i próbując otypować onSubmit, możemy przez chwilę czuć się zakłopotani. Coś nad czym nawet nie trzeba myśleć w JavaScriptcie, w przypadku TypeScripta wymaga albo spotkania się z tym wcześniej, analizy tego co mamy w typach dostarczonych przez Reacta albo poszukanie czegoś w internetach.

Poniżej przykład prostego formularza bez typów, z jednym listenerem nasłuchującym na zdarzenie onSubmit, bez całej otoczki związanej z innymi handlerami i stanem. Pozwoli nam się to skupić na jednej rzeczy.

const handleSubmit = (event) => {
  event.preventDefault()
  // tutaj mamy dostęp do wartości z formularza,
  // które ewentualnie możemy przesłąć dalej lub zrobić z nimi coś innego
  console.log(event.currentTarget.elements)
}

const Login = () => {
  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor="name">Nazwa użytkownika:</label>
        <input id="name" type="text" name="name"/>
      </div>
      <div>
        <label htmlFor="password">Hasło:</label>
        <input id="password" type="password" name="password"/>
      </div>
      <div>
        <label htmlFor="rememberMe">Zapamiętaj mnie</label>
        <input type="checkbox" id="rememberMe" name="rememberMe" />
      </div>
      <button type="submit">Zaloguj</button>
    </form>
  )
}

onSubmit typowanie handlera

Żeby mieć dostęp do elementów, które umieściliśmy w formularzu trzeba je otypować. Mamy dwie możliwości albo opisać event albo zdefiniować typ dla handlera. My skorzystamy z tej drugiej opcji.

// przykład otypowania eventu
const handleSubmit = (event: React.FormEvent<LoginFormElement>) => {}

// przykład otypowania handlera
const handleSubmit: React.FormEventHandler<LoginFormElement> = (event) => {}

Poprzez zastosowanie jednego z powyższych rozwiązań definiujemy typ elementu dla event.currentTarget, którym jest formularz czyli HTMLFormElement. Ma on dostęp do właściwości elements, jej typ to interface HTMLFormControlsCollection. Musimy go rozszerzyć o pola, które dodaliśmy do naszego formalarza. Oczywiście można by to było zrobić za pomocą asercji bezpośrednio w handlerze, bez tego co sugeruję w tym poście ale używanie tego w każdym formularzu może być kłopotliwe i nie wygląda zbyt dobrze. Po opisaniu typów pól możemy bez problemy zacząć używać elements z podpowiedziami.

Cały formularz razem z typami:

import React from 'react';

// ----
// TYPY
// ----
interface FormElements extends HTMLFormControlsCollection {
  name: HTMLInputElement;
  password: HTMLInputElement;
  rememberMe: HTMLInputElement;
}

interface LoginFormElement extends HTMLFormElement {
 readonly elements: FormElements
}

type THandleSubmit = React.FormEventHandler<LoginFormElement>;

// -------
// HANDLER
// -------
const handleSubmit: THandleSubmit = (event) => {
  event.preventDefault()

  console.log(event.currentTarget.elements.name.value)
  console.log(event.currentTarget.elements.password.value)
  console.log(event.currentTarget.elements.rememberMe.checked)
}

// ---------
// FORMULARZ
// ---------
const Login = () => {
  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor="name">Nazwa użytkownika:</label>
        <input id="name" type="text" name="name"/>
      </div>
      <div>
        <label htmlFor="password">Hasło:</label>
        <input id="password" type="password" name="password"/>
      </div>
      <div>
        <label htmlFor="rememberMe">Zapamiętaj mnie</label>
        <input type="checkbox" id="rememberMe" name="rememberMe" />
      </div>
      <button type="submit">Zaloguj</button>
    </form>
  )
}

export default Login;