Jak zacházet s formuláři jen s React

Ok, název tohoto článku je návnada. Ve skutečnosti vám řeknu o tom, jak lze s formuláři zacházet ... Javascript. A reagovat nám jen pomůže zůstat deklarativním. K tomu je konec konců.

Pokud jste někdy měli pocit, že práce s formuláři v Reactu byla příliš velká, nebo že možná hranatá byla zvyklá na lepší zážitek, nebo si jen přejete vědět, jaký by mohl být nejlepší způsob organizace forem v reakci, pak si přečtěte dále.

První věc, kterou bych chtěl zmínit, je, jak silný a podceňovaný DOM API je. Má však poněkud špatnou pověst, protože je to naprosto nezbytné. To je důvod, proč knihovny jako reakce, vue, úhlové atd. Existují na prvním místě: odstranit abstraktní imperativní povahu DOM api. Myslím, že to vytváří špatný dojem s začátečníky, kteří skočí k závěru, že byste se měli vyhýbat tomu. Měli byste se opravdu vyhnout imperativnímu kódu, ale měli byste také přijmout veškerou sílu, kterou vám prohlížeč a DOM poskytují.

Dobře, s úvodem, skočme dovnitř.

Jak vytvořit formuláře s reakcí a odesláním dat na server?

Začněme jednoduše.

Nej minimalistický přístup

Ukázka:

A tady je kód:

Dobře, kde jsou atributy hodnot nebo zpětná volání onChange? No, nemusíte je používat. Zpětné volání onSubmit se nazývá, když odešlete formulář html kliknutím na tlačítko Odeslat nebo jednoduše stisknutím klávesy „Enter“, zatímco je zaostřeno v jednom ze vstupních polí. Když do svých vstupů přidáte atributy názvu, přidáte do formuláře strukturu. Tuto strukturu lze serializovat nativním rozhraním FormData (základní podpora ve všech prohlížečích a IE10 +). Vše, co uděláte, je předat elementu formuláře (ke kterému přistupujeme přes event.target) konstruktoru FormData a získáte serializovanou interpretaci vstupů, které lze odeslat na server.

Všimněte si také, že k tlačítku nepřidáváme posluchače onClick. Pokud bychom tak učinili, nebyli bychom schopni reagovat na odeslání událostí spuštěných z klávesnice (stisknutím klávesy Enter). To je špatné UX. Použitím zpětného volání onSubmit pokryjeme oba případy.

Při použití této metody, bez ohledu na to, jak se váš formulář zvětší, nemusíte psát žádný další kód. Řiďte se osvědčeným postupem, kdy do svých vstupních značek vždy přidáváte atribut name (tato jména by samozřejmě měla odpovídat tomu, co server očekává).

Všimněte si také toho, jak jsme udržovali naši komponentu formuláře plně deklarativní, a to i bez použití takových reakčních funkcí jako „řízené komponenty“. Nejsou potřeba žádné odkazy ani hrubé DOM manipulace. Zde je odkaz na housle s formulářem.

Moje data vyžadují transformaci ze vstupu uživatele, takže potřebuji stavové a složité kontrolované komponenty!

No, ne, ne nutně.

Jednou z prvních věcí, které se naučíte, když začnete s reakcí, je, že data by měla mít jediný zdroj pravdy a že by měla proudit jedním směrem, shora dolů. To je pravda. Ale kde je „zdroj“ dat formuláře? Záleží na druhu aplikace, kterou máte.

Je velmi pravděpodobné, že data formuláře pocházejí z uživatelského vstupu a odnikud nikde a nikde jinde se nesdílejí.

Jednou z cenných lekcí, které se naučíte z reakčních dokumentů, je, že když musíte sdílet stav, měli byste jej zvednout. Ale buďte opatrní: nezvedejte stát, když to nepotřebujete.

Ano, existují případy, kdy jsou regulované vstupy platnou volbou. Tyto případy plánuji zahrnout do příštího příspěvku.

Ale prozatím bych rád prozkoumal, jak daleko můžete jít s výše popsaným jednoduchým přístupem, aniž byste se natáhli pro kontrolované vstupy.

Transformace vstupních dat

Představte si, že data, která server potřebuje, jsou v jiné formě než data, která uživatel zadá. Řekněme, že máme tyto požadavky:

  • uživatel zadá datum ve formátu MM / DD / RRRR, ale server jej očekává ve formátu RRRR-MM-DD
  • uživatelské jméno by mělo být zasláno velkými písmeny
handleSubmit (event) {
    event.preventDefault ();
    const data = new FormData (event.target);
    // POZNÁMKA: Do polí FormData vstupujete pomocí `data.get (fieldName)`
    const [měsíc, den, rok] = data.get ('datum narození'). split ('/');
    const serverDate = `$ {year} - $ {month} - $ {day}`;
    data.set ('datum narození', serverDate);
    data.set ('username', data.get ('username'). toUpperCase ());
    načíst ('/ api / form-submit-url', {
      metoda: POST,
      tělo: data,
    });
}

To bylo velmi snadné. Nejlepší na tom je, že jste nemuseli hledat určitý rámec nebo plugin specifický způsob, jak toho dosáhnout. Pamatujete si $ parsers a $ formatters pipeline? Nebyla to špatná vlastnost, byla to skvělá funkce. Užíval jsem si to. Ale myslím, že budete souhlasit s tím, že v tomto konkrétním případě jde o přebytek.

Existuje však jedna nevýhoda. Všimli jste si, jak jsme se spojili s určitými vstupy? V obslužném programu handleSubmit nyní musíme vědět, které vstupy je třeba transformovat a které nikoli. Ztratili jsme nějakou deklarativitu. Řekněme tento problém.

od této chvíle, kdy je třeba transformovat hodnotu zadanou uživatelem, zavolám tuto „analýzu“.

V závislosti na vaší aplikaci budete možná potřebovat různé funkce analyzátoru pro různé druhy vstupů. Ale v celé své aplikaci budete pravděpodobně znovu používat mnoho z nich. Opakované použití kódu je něco, co děláme pořád. Proč nevytvářet sadu obslužných funkcí, které jsou zodpovědné za analýzu vstupů do formuláře? Zde je příklad:

const inputParsers = {
  datum (vstup) {
    const [měsíc, den, rok] = input.split ('/');
    návrat `$ {year} - $ {month} - $ {day}`;
  },
  velká písmena (vstup) {
    return input.toUpperCase ();
  },
  číslo (vstup) {
    návrat parseFloat (vstup);
  },
};

Pouze javascriptový objekt s metodami. Ale jak to používáme? Dobře…

Je čas zjistit více o DOM api

Jakýkoli element

má vlastnost elements. Přečtěte si o tom více zde. Je to objekt kolekce html. Nejlepší na tom je, že poskytuje přístup ke všem vstupním uzlům formuláře klíčem, kde klíč je atribut názvu vstupu. Ano, bez ohledu na to, jak hluboko uvnitř formuláře máte prvek , můžete k němu přistupovat pomocí form.elements.birthdate. Není to skvělé?

Ok, můžeme přistupovat ke všem vstupním uzlům formuláře podle jejich názvu. Jak ale víme, které z nich máme analyzovat? Jak můžeme označit ty vstupy, které potřebují další analýzu?

Datové atributy samozřejmě! Pro to jsou tady. Chcete-li tedy naznačit, že hodnota vstupu musí být převedena na velká písmena, než ji odešleme na server, navrhuji do něj přidat atribut data-parse:

Velmi popisné. A tady je celý příklad ukazující, jak můžeme transformovat všechna potřebná data. Věnujte pozornost manipulátoru „handleSubmit“:

To je podle mě docela silné. Opět platí, že naše formy mohou růst tak velké, jak potřebují, aniž by se naše obsluha zvětšila.

A nejlepší na tom je, že nejenže jsme se nevázali na žádnou knihovnu pro zpracování reakcí, ale stěží jsme se vázali, abychom reagovali. Pokud se jednoho dne rozhodnete z jakéhokoli důvodu odejít, nemusíte výrazně změnit způsob, jakým se zabýváte formuláři. DOM nikam nevede.

Ověření vstupu

Pokud jste pozorní, možná jste si všimli jiného problému s výše uvedeným formulářem. Před jejich analýzou nekontrolujeme platnost vstupů, což může vést k chybám. Pokud si myslíte, že nám DOM api nemůže pomoci, nebo že není dobře podporována, ráda vám řeknu, že se mýlíte. Html forma validace je další mocná věc, kterou se mi moc líbí.

Nejjednodušší příklad:


  

A je to. Ke vstupu jsme přidali pouze požadovaný atribut. Prohlížeč bude považovat toto vstupní pole za neplatné, pokud je prázdné a platné, pokud má alespoň jeden znak. Pokud je alespoň jedno ze vstupních polí neplatné, prohlížeč nedovolí uživateli odeslat formulář, místo toho zobrazí popis poblíž prvního neplatného vstupu.

Jde však o to, že se na toto chování nemůžeme spolehnout: prohlížeč nezabrání odeslání formuláře v Safari a mobilním safari. Ale to nemusíme! Popisy prohlížeče nejsou přesto nejlepším způsobem, jak jít: nejsou dostatečně flexibilní a nejsou snadno stylizovatelné.

Co děláme je toto:


  

Přidáme atribut novalidate (noValidate v jsx se změní na novalidate v html). Název atributu je poněkud zavádějící. Když ji přidáme, ve skutečnosti nevypínáme ověřování formulářů. Prohlížeči bráníme v zasahování pouze v případě, že je odeslán neplatný formulář, abychom mohli sami „zasahovat“.

Jak tedy nyní funguje ověřování formulářů? Takto: element

má metodu checkValidity (), která vrací false, když je formulář považován za neplatný, a true, když je platný. Formulář je považován za neplatný, pokud je alespoň jeden z jeho vstupních prvků neplatný. Tady je malý demo-gif:

A takto to můžeme použít v naší metodě handleSubmit:

handleSubmit (event) {
  if (! event.target.checkValidity ()) {
    // formulář je neplatný! takže neděláme nic
    vrátit se;
  }
  // formulář je platný! Můžeme analyzovat a odesílat data
}

Skvělý! Nyní máme způsob, jak zabránit analýze neplatných vstupů, když formulář není platný.

Můžeme zjistit jednotlivé neplatné vstupy? Tak určitě! Jak? Přesně stejné: vstupy mají také metodu .checkValidity (). Podívej se sám:

Přidání požadovaného atributu není jediným způsobem, jak prohlížeči sdělit, že je třeba zkontrolovat vstup. Co jiného můžeme udělat?

  • Použijte atribut pattern. Nejsilnější atribut ověření. Jeho hodnota by měla být regex, který bude porovnáván s celou vstupní hodnotou.
    Řekněme, že chceme povolit pouze čísla v některém vstupním poli. Postupujeme takto: . A je to. Nyní můžeme zavolat metodu .checkValidity () vstupu a zkontrolovat, zda je platná nebo ne.
Pokud je vstup prázdný, považuje se za „platný“, i když neodpovídá regexu. Je to proto, že jsme vynechali atribut „požadovaný“.
  • Zadejte vstup typový e-mail. Stejně jako název napovídá, bude vstup zkontrolován na platný vzor e-mailu, takže nemusíme přijít s nepředvídatelným regexovým řešením.

Dobře, využijte tyto znalosti. Zde může vypadat náš kód formuláře:


  
  
  
  
  
  
  

Zde je kompletní přehled o ověření a zde je stručný pohled na to, jak to může fungovat:

Vizualizace neplatných vstupů

Vím, co si myslíš. Pokud lze platnost vstupu zkontrolovat pomocí metody .checkValidity (), můžeme pomocí javascriptu přepínat třídy platnosti!

Špatně! No, to opravdu není „špatné“, ale mám mnohem lepší řešení, které mohu nabídnout. Pojďme zjistit ještě jednu silnější věc o formuláři API.

Volič: neplatný css

Na ty vstupy, které jsou neplatné, lze cílit čistě css! Stejně jako toto:

vstup: neplatný {
  barva okraje: červená;
}

Opět vím, co si možná myslíte. "Taková magie css má pravděpodobně hroznou podporu prohlížeče". Jsem tak rád, že vás opravím: má skvělou podporu prohlížeče!

Existují však určité nevýhody. I když je tento css pseudo-selektor silný, je také docela němý. Řekněme, že chceme stylizovat neplatné vstupy až poté, co se uživatel pokusí odeslat formulář. Formulář nemá ponětí, že je „špinavý“ nebo že má „pokus o předložení“. Neplatné vstupy budou tedy označeny červeným okrajem ještě předtím, než se uživatel pokusí něco napsat.

Jak se s tím vypořádat? Velmi lehce! Poté, co se uživatel pokusí odeslat a označit vstupy jako neplatné pouze v případě, že jsou uvnitř formuláře s třídou .displayErrors, můžeme do formuláře přidat pouze třídu „displayErrors“:

handleSubmit (event) {
  if (! event.target.checkValidity ()) {
    this.setState ({displayErrors: true});
    vrátit se;
  }
  this.setState ({displayErrors: false});
}
poskytnout() {
  const {displayErrors} = this.state;
  vrátit se (
    
      {/ * ... * /}
    
  );
}

… A v našem CSS:

.displayErrors input: invalid {
  barva okraje: červená;
}

Funguje jako kouzlo:

Tady je fiddle, se kterým si můžeš hrát.

Doufám, že jsem vás přesvědčil, že nativní DOM API je mocná věc, která nedostává dostatečnou pozornost a že byste ji měli používat. Je dostatečně flexibilní, aby vám poskytl jakékoli chování, které si přejete.

A co „řízené“ vstupy? Existuje mnoho případů, kdy je potřebujete. V tomto příspěvku jsem vás chtěl seznámit s API API formulářů a prozkoumat jeho sílu. K tomuto účelu slouží velmi dobře nepoužívané kontrolované vstupy.

Možná si myslíte, že když máte „kontrolované“ vstupy, toto DOM API nepotřebujete. Ujišťuji vás však, že se tyto věci vzájemně doplňují. Ve svém dalším příspěvku chci prozkoumat, jak vás použití nativních formulářů API spolu s kontrolovaným vstupem může ještě zvýšit. Chtěl bych však také zdůraznit, že byste neměli hledat silnější nástroj, když se bez něj můžete snadno obejít.