Poznajemy obiekt FormData w JS oraz to, jak możemy z niego skorzystać w React. Do dzieła.

Ok, obiekt FormData przyjmuje formularz, oraz opcjonalnie submitter (submit button):

const form = document.getElementById("form");
const submitter = document.querySelector("button[value=save]");
const formData = new FormData(form, submitter);

Obiekt posiada iterator i możemy iterować po tym:

const output = document.getElementById("output");

for (const [key, value] of formData) {
  output.textContent += `${key}: ${value}\n`;
}

Już teraz powinniśmy się domyślać, że iterator zwraca pseudotuple klucz-wartość. FormData posiada metodę append:

formData.append("username", "Chris");

Key (atrybut name w HTMLInputElement) i value. Jeżeli value to blob (plik) to jeszcze mamy opcjonalne filename:

formData.append("userpic", myFileInput.files[0], "chris.jpg");

Inne metody:

  • fd.has(key) – zwraca czy ma taką wartość
  • fd.delete(key) – usuwa z formdata taką wartość
  • fd.get(key) – pobiera wartość o podanym kluczu
  • fd.set(key,val) – ustawia wartość o kluczu i val jak podane
  • fd.set(key,blob, filename) – to do plików, jak val jest blobem podajemy opcjonalnie filename, inaczej nazywa się blob (dla blobów utworzonych w pamięci przez konstruktor) lub jeśli jest plikiem uploadowanym formularzem to bierze od pliku filename
  • fd.getAll(key) – w przypadku każdego HTMLInputElement zwraca wynik jako tablicę, w przypadku type checkbox to jedyny sposób na to, aby uzyskać wszystkie zaznaczone elementy o takim samym name

Pamiętajmy, nazwa klucza w FormData to jest atrybut name w HTMLInputElement. Tylko te elementy mają taki atrybut, mają też value (to jest wartość) oraz inne atrybuty typu class, id, standardowe dla HTMLElement.

Ok, oto przykład użycia FormData w React:

import { useState } from 'react';

export default function Signup() {
  const [passwordsAreNotEqual, setPasswordsAreNotEqual] = useState(false);

  function handleSubmit(event) {
    event.preventDefault();

    const fd = new FormData(event.target);
    const acquisitionChannel = fd.getAll('acquisition');
    const data = Object.fromEntries(fd.entries());
    data.acquisition = acquisitionChannel;

    if (data.password !== data['confirm-password']) {
      setPasswordsAreNotEqual(true);
      return;
    }

    console.log(data);
  }

  return (
    <form onSubmit={handleSubmit}>

Czyli co się dzieje:

  • Mamy stan, który ma pokazywać, czy jest ok z hasłami czy nie
  • Mamy form z atrybutem onSubmit, który pokazuje na handler
  • Mamy handler, który po pierwsze robi prevent default (blokuje odświeżanie strony)
  • tworzymy fd, event.target to form
  • Bierzemy jakieś dane z checkboxów (inaczej nie robilibyśmy fd.getAll)
  • Bierzemy wszystkie dane z formularza robiąc z nich obiekt, do którego wrzucamy fd.entries (które ma pseudotuple name-value)
  • Wiemy, że fd.entries ogarnie tylko proste rzeczy typu name-value. I zamieni nam na obiekt, ale pod tym acquisition to ktoś mógł kilka rzeczy zaznaczyć (checkbox) i będzie tylko jedno value (raczej ostatnie)
  • Całe szczęście już to acquisition sobie pobraliśmy jako tablicę poprawnie, więc pozostaje tylko nadpisać
  • Dalej jest sprawdzanie, czy hasła są ok, zmiana stanu jak nie i return
  • Dalej console.log bo to raczej jakaś treningowa lekcja niż skończony projekt (kod znaleziony na Githubie)

I to by było na tyle, dodam od siebie, że React pracuje nad eksperymentalnym, nowym hookiem useActionState, który, zdaje się, będzie korzystać z obiektu FormData, więc to nie są takie głupoty wymyślane przez ludzi, którym się nudzi, to wszystko warto znać, trzeba znać i bardzo ułatwia pracę.