Как да боравите с формуляри само с React

Ок, заглавието на тази статия е стръв. Всъщност ще ви разкажа как могат да се обработват формуляри с ... Javascript. И реагирането просто ще ни помогне да останем декларативни. В крайна сметка това е

Ако някога сте имали усещането, че работата с формуляри в React е прекалено голяма котлова или че може би е ъглова, използвана за осигуряване на превъзходно изживяване или просто искате да знаете какъв е най-добрият начин * за организиране на формуляри в реакция, след това прочетете.

Първото нещо, което искам да спомена е колко мощен и подценен е DOM API. Има малко лоша репутация, защото е напълно наложително. Ето защо на първо място съществуват библиотеки като реагиране, vue, angular и т.н.: за да се абстрахира императивната природа на DOM api. Мисля, че това създава погрешно впечатление у начинаещите, които скачат до извода, че трябва да стоите далеч от него. Наистина трябва да избягвате императивния код, но трябва също така да прегърнете цялата сила, която ви дават браузърът и DOM.

Добре, достатъчно с въведение, нека влезем

Как да създадете формуляри с реакция и изпращане на данни до сървъра?

Да започнем просто.

Най-минималистичният подход

Демонстрация:

И ето кода:

Добре, къде са стойностните атрибути или обратните извиквания onChange? Е, не е необходимо да ги използвате. OnSubmit callback се обажда, когато изпратите html формата, като кликнете върху бутона за изпращане или просто като натиснете „Enter“, докато сте фокусирани в едно от полета за въвеждане. Когато добавите атрибути на име към вашите входове, вие добавяте структура към формата си. Тази структура може да бъде сериализирана от родния интерфейс на FormData (основна поддръжка във всички браузъри и IE10 +). Всичко, което правите, е да преминете във формуляр елемент (до който осъществяваме достъп чрез event.target) към конструктора на FormData и получавате сериализирана интерпретация на входовете, които могат да бъдат изпратени до сървъра.

Също така забележете, че не добавяме слушател onClick към бутона. Ако го направихме, няма да можем да отговорим на изпращането на събития, задействани от клавиатурата (чрез натискане на Enter). Това е лошо UX. Използвайки обратното обаждане на onSubmit, ние покриваме и двата случая.

Използвайки този метод, без значение колко голяма е вашата форма, не е необходимо да пишете допълнителен код на кокетната плоча. Просто следвайте добра практика винаги да добавяте атрибут на име към вашите входни маркери (разбира се, че тези имена трябва да съответстват на това, което сървърът очаква).

Също така забележете как запазихме изцяло декларативния си компонент, дори без да използваме такива функции на реакция като „контролирани компоненти“. Не са необходими реф. Или сурови DOM манипулации. Ето връзката към скрипта с формата.

Моите данни изискват да бъдат трансформирани от потребителски вход, така че имам нужда от състояния и сложни контролирани компоненти!

Е, не, не е задължително.

Едно от първите неща, които научаваш, когато започваш с реакция, е, че данните трябва да имат един източник на истина и че те трябва да протичат по един път, отгоре надолу. Вярно е. Но къде е „източникът“ на данните от формуляра? Зависи от вида на приложението.

Много е вероятно данните за формата да идват от въвеждането на потребителя и от никъде другаде и не се споделят никъде другаде.

Един от ценните уроци, които научавате от реагиращите документи, е, че когато трябва да споделяте състояние, трябва да повдигнете държавата. Но бъдете внимателни: не вдигайте държавата, когато не е необходимо.

Да, има случаи, когато контролираните входове са валиден избор. Смятам да обхвана тези случаи в следващия си пост.

Но засега бих искал да проуча докъде можете да стигнете с простия подход, описан по-горе, без да посягате към контролирани входове.

Преобразувания на входните данни

Представете си, че данните, от които се нуждае сървърът, са в различна форма от данните, които потребителят въвежда. Нека да кажем, че имаме тези изисквания:

  • потребителят въвежда датата във формат MM / DD / ГГГГ, но сървърът я очаква в ГГГГ-ММ-ДД
  • потребителското име трябва да бъде изпратено с големи букви
handleSubmit (събитие) {
    event.preventDefault ();
    данни за const = нов FormData (event.target);
    // ЗАБЕЛЕЖКА: имате достъп до полета FormData с „data.get (fieldName)“
    const [месец, ден, година] = data.get ('дата на раждане'). split ('/');
    const serverDate = `$ {година} - $ {месец} - $ {ден}`;
    data.set ('дата за раждане', serverDate);
    data.set ('потребителско име', data.get ('потребителско име'). toUpperCase ());
    извлечете ('/ api / form-submit-url', {
      метод: „POST“,
      тяло: данни,
    });
}

Това беше много лесно. Най-хубавото в това е, че не е нужно да търсите конкретен за рамка или плъгин начин, за да направите това. Спомняте ли си тръбопровод $ parsers и $ формати? Това не беше лоша характеристика, а чудесна функция. Хареса ми да го използвам. Но мисля, че ще се съгласите, че това е излишно за този конкретен случай.

Има обаче един недостатък. Забелязахте ли как сме се обвързали с конкретни данни? Вътре в дръжката за подаване на дръжка сега трябва да знаем кои входове трябва да бъдат трансформирани и кои не. Изгубихме известна декларативност. Нека решим този проблем.

отсега нататък, когато въведената от потребителя стойност трябва да бъде трансформирана, ще наричам това „синтактичен анализ“.

В зависимост от приложението ви може да ви трябват различни функции за анализиране на различни входове. Но в приложението ви вероятно ще използвате повторно много от тях. Използването на повторно код е нещо, което правим непрекъснато. Защо да не създадете набор от помощни функции, които са отговорни за анализа на вашите формуляри? Ето пример:

const inputParsers = {
  дата (въвеждане) {
    const [месец, ден, година] = input.split ('/');
    връщане `$ {година} - $ {месец} - $ {ден}`;
  }
  главни букви (вход) {
    връщане input.toUpperCase ();
  }
  номер (вход) {
    връщане parseFloat (вход);
  }
};

Просто обект на JavaScript с методи. Но как да го използваме? Добре…

Време е да разберете повече за DOM api

Всеки

елемент има свойство на елементи. Прочетете повече за това тук. Това е html обект за събиране Най-хубавото в него е, че осигурява достъп до всички въвеждащи възли на формата чрез ключ, където ключът е атрибут на името на входа. Да, независимо колко дълбоко във формата имате елемент , можете да получите достъп до него с form.elements.birthdate. Не е ли така страхотно?

Добре, можем да получим достъп до всички въвеждащи възли на формата от тяхното име. Но как да разберем кои да разберем? Как можем да маркираме тези входове, които се нуждаят от допълнителен анализ?

Данните-атрибути разбира се! За това са тук. Така че за да посочите, че стойността на входа трябва да се трансформира в големи букви, преди да го изпратим на сървъра, предлагам да добавите към него атрибут за анализ на данните:

<вход
  име = "потребителско име"
  тип = "текст
  данни синтактична = "главна"
/>

Много описателен. Ето и целия пример, който показва как можем да трансформираме всички необходими данни. Обърнете внимание на манипулатора на "handleSubmit":

Според мен това е доста мощно За пореден път формулярите ни могат да станат толкова големи, колкото им е необходимо, без да увеличаваме функцията на манипулатора.

И най-хубавото е, че не само, че не се обвързахме с никоя библиотека за обработка на реакции, ние трудно се обвързахме с реакция. Ако един ден решите да се откажете да реагирате по някаква причина, не е необходимо да променяте забележимо начина на работа с формулярите. DOM не отива никъде.

Проверка на входа

Ако наблюдавате, вероятно сте забелязали друг проблем с горната форма. Не проверяваме входовете за валидност преди да ги анализираме, което може да доведе до грешки. Ако смятате, че DOM api не може да ни помогне тук или че не се поддържа добре, ще се радвам да кажа, че грешите. Утвърждаването на Html формуляр е друго мощно нещо, което много харесвам.

Най-простият пример:

<Форма>
  

Това е. Добавихме само задължителен атрибут към вход. Браузърът ще счита това поле за въвеждане за невалидно, ако е празно и валидно, ако има поне един знак. Когато поне на полетата за въвеждане е невалиден, браузърът няма да позволи на потребителя да изпрати формуляра, вместо това ще покаже подсказка близо до първия невалиден вход.

Но нещата не могат да разчитат на това поведение: браузърът няма да попречи на формуляра да бъде изпратен в Safari и мобилно сафари. Но не е нужно! Съвети за браузъри не са най-добрият начин за всеки случай: те не са достатъчно гъвкави и не могат лесно да се стилизират.

Това, което правим е това:

<форма noValidate>
  

Добавяме атрибут novalidate (noValidate в jsx се превръща в novalidate в html). Името на атрибута е донякъде подвеждащо. Когато го добавим, всъщност не изключваме валидирането на формата. Ние не позволяваме на браузъра да се намесва, когато е изпратен невалиден формуляр, така че да можем да се намесваме сами.

И така, как работи валидирането на формата сега? По този начин: елементът има метод checkValidity (), който връща false, когато формата се счита за невалидна и true, когато е валидна. Формата се счита за невалидна, когато поне един от входните й елементи е невалиден. Ето един малък демо-gif:

И ето как можем да използваме това в нашия метод на дръжка:

handleSubmit (събитие) {
  ако (! event.target.checkValidity ()) {
    // формулярът е невалиден! така че не правим нищо
    се върне;
  }
  // формулярът е валиден! Можем да анализираме и да изпратим данни
}

Страхотен! Сега имаме начин да предотвратим анализа на невалидни входове, когато формулярът не е валиден.

Можем ли да открием отделни невалидни входове? Сигурен! Как? Точно същото: входовете имат и метод .checkValidity (). Вижте сами:

Добавянето на необходим атрибут не е единственият начин да се каже на браузъра, че трябва да бъде проверен входът. Какво друго можем да направим?

  • Използвайте атрибута на шаблон. Най-мощният атрибут за валидиране Стойността му трябва да бъде регекс, който ще бъде съпоставен с цялата входна стойност.
    Да речем, че искаме да разрешим само числа в някакво поле за въвеждане. Ето как можем да го направим: . Това е. Вече можем да се обадим на .checkValidity () метод на входа и да проверим дали е валиден или не.
Обърнете внимание, че когато входът е празен, той се счита за „валиден“, въпреки че не съвпада с регулярното изражение. Това е така, защото пропуснахме атрибута „задължително“.
  • Въведете имейл тип тип. Точно както името подсказва, входът ще бъде проверен за валиден шаблон на имейл, така че не е необходимо да измисляме странно регекс.

Добре, нека използваме тези знания. Ето как може да изглежда нашият формуляр код сега:

<форма onSubmit = {this.handleSubmit} noValidate>
  
  <вход
    ID = "потребителско име"
    име = "потребителско име"
    тип = "текст"
    данни синтактична = "главна"
  />
  
  
  
  <вход
    ID = "дата на раждане"
    име = "дата на раждане"
    тип = "текст"
    данни синтактична = "дата"
    Заместител = "ММ / ДД // YYYY"
    модел = "\ г {2} \ / \ г {2} / \ г {4}"
    длъжен
  />
  

Ето пълна пъзел с валидиране и ето кратък преглед как може да работи:

Визуализиране на невалидни входове

Знам какво може да мислите. Ако валидността на входа може да бъде проверена чрез метод .checkValidity (), тогава можем да променим класовете за валидност с javascript!

Грешка! Е, това всъщност не е „грешно“, но имам много по-добро решение. Нека разберем още едно мощно нещо за API на формата.

:: Невалиден css селектор

Тези невалидни данни могат да бъдат насочени с чист css! Просто така:

вход: невалиден {
  цвят на рамката: червен;
}

Отново знам какво може да мислите. „Подобна css магия вероятно има страшна поддръжка на браузъра“. Толкова се радвам да ви поправя: има прекрасна поддръжка на браузър!

Има обаче някои недостатъци. Въпреки че този псевдоселектор css е мощен, той също е доста тъп. Да речем, че искаме да стилизираме невалидни данни само след като потребителят се опита да изпрати формуляра. Формулярът няма понятие, че е "мръсен" или има състояние "опитано да се изпрати". Така невалидните входове ще бъдат маркирани с червена рамка, дори преди потребителят да е опитал да въвежда нещо изобщо.

Как можем да се справим с това? Много лесно! Можем просто да добавим клас „displayErrors“ към самата форма, след като потребителят се опита да го изпрати и да стилизира входовете като невалидни, само когато са във форма с .displayErrors клас:

handleSubmit (събитие) {
  ако (! event.target.checkValidity ()) {
    this.setState ({displayErrors: true});
    се върне;
  }
  this.setState ({displayErrors: false});
}
render () {
  const {displayErrors} = this.state;
  връщане (
    <форма
      onSubmit = {this.handleSubmit}
      noValidate
      className = {displayErrors? 'displayErrors': ''}
    >
      {/ * ... * /}
    
  );
}

... и в нашия css:

.displayErrors input: невалиден {
  цвят на рамката: червен;
}

Работи като чар:

Ето ти скрипката, с която трябва да играеш.

Надявам се да съм ви убедил, че родният DOM API е мощно нещо, което не привлича достатъчно внимание и че трябва да го използвате. Достатъчно гъвкава е, за да ви даде каквото поведение желаете.

Какво ще кажете за контролираните входове? Има много случаи, когато имате нужда от тях. В тази публикация исках да ви запозная с API на формите и да проуча неговата сила. Използването на контролирани входове служи за тази цел много добре.

Може да си мислите, че когато имате „контролирани“ входове, нямате нужда от този DOM API. Но ви уверявам, че тези неща се допълват. В следващия си пост искам да проуча как използването на родните форми api заедно с контролирано въвеждане може да ви направи още по-мощни. Но искам също да подчертая, че не бива да търсите по-мощен инструмент, когато лесно можете да го направите без него.