Абстрагирование логики изменения ввода


Сократите использование шаблонных форм!

Как начинающий программист, мы изучаем более простые пути, как заставить все работать на React, прежде чем перейти к более чистому, сухому и лаконичному коду. Ну, как начинающий программист вы, наверное, уже знакомы с тем, как создать форму с помощью React, верно? И я уверен, что вы хорошо знакомы с тем, как установить состояние каждого вводимого значения или как создать функцию для перехвата всех этих вводимых значений, чтобы изменить состояние, да? И я уверен, что вы следуете лучшим практикам кодирования, сохраняя состояние и функцию для обработки этого состояния в одном компоненте. Или что вы также устанавливаете функцию для обработки события onChange и Submit вне JSX, верно? Или даже то, что вы используете управляемые входы на своей форме, верно? Да, я уверен, что мы все в курсе, и что ваш код выглядит примерно так:

Стартовый код

import React, { useState } from "react";

function Form() {

  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const [avatar, setAvatar] = useState("");
  const [mailingList, setMailingList] = useState(false);

  const handleUsername = (e) => {
    setUsername(e.target.value)
  } 

  const handlePassword = (e) => {
    setPassword(e.target.value)
  }

  const handleAvatar = (e) => {
    setAvatar(e.target.value)
  } 

  const handleMailingList = (e) => {
    setMailingList(e.target.checked)
  } 

  const handleSubmit = (event) => {
    event.preventDefault();
    const formData = {
     username,
     password,
     avatar,
     accountType,
     newsletter
  }
  console.log(formData);
  }



  return (
    <form onSubmit={handleSubmit}>
      <h1>Create an Account</h1>
      <label htmlFor="username">Username</label>
      <input
        type="text"
        id="username"
        value={username}
        onChange={handleUsername}
      />

      <label htmlFor="password">Password</label>
      <input
        type="password"
        id="password"
        value={password}
        onChange={handlePassword}
      />

      <label htmlFor="avatar">Avatar Image</label>
      <input
        type="text"
        id="avatar"
        value={avatar}
        onChange={handleAvatar}
      />
      <img src="https://i.pinimg.com/originals/0a/dd/87/0add874e1ea0676c4365b2dd7ddd32e3.jpg"
        alt="Avatar preview"
      />

      <label>
        Join our Mailing List!
        <input
          type="checkbox"
          id="mailingList"
          checked={mailingList}
          onChange={handleMailingList}
        />

      </label>
      <input type="submit" value="Sign Up" />
    </form>

  )
}

export default Form
Вход в полноэкранный режим Выйти из полноэкранного режима

Ух ты! Вот это зверь… Это типичная кодовая табличка, вырезать, скопировать, alt + кликнуть все эти строки, и вставить форму по существу. Но для новичка это очень полезно знать! Выучите это до такой степени, чтобы это стало мышечной памятью. Тогда вы будете готовы перейти к более динамичным формам, которые могут адаптироваться, если вы захотите изменить форму, чтобы собрать больше данных о пользователе. Давайте разберемся с этим подробнее.

Установите состояние

Сначала начнем со всех этих маленьких кусочков состояния для каждого поля ввода. Мы собираемся взять все эти состояния и объединить их в одно, инициализировав состояние в объект с ключами, каждый из которых имеет начальный фрагмент состояния. Назовем этот новый фрагмент состояния formData, так как в нем будут храниться все данные нашей формы. Примерно так:



Как вы видите, мы берем все эти фрагменты состояния из оранжевого блока, помещаем их под один фрагмент состояния в желтом блоке и инициализируем его как объект с ключевыми значениями, установленными на пустые строки и ложное состояние для списка рассылки, как это было раньше, когда все они сидели в своих собственных состояниях. Это уже начинает выглядеть менее беспорядочно, да? Давайте двигаться дальше.

Следующее, что нам нужно сделать, это посмотреть на наш JSX, где задаются значения для каждого входа. Как вы можете видеть, ранее мы устанавливали значение в состояние, которое у нас было изначально, но теперь, когда мы сократили наше состояние до formData, давайте теперь подключим это состояние и, используя точечную нотацию, установим наше состояние в ключевое значение username. Нам нужно будет вставить это значение для всех наших значений.



Я хотел бы отметить, что значение для флажка — это checked, а не value, так что имейте это в виду, когда мы будем двигаться дальше.

Разберите функции обработчика

Лучшая практика диктует, чтобы состояние и его функции-обработчики были обернуты в один компонент, поэтому мы следуем этому принципу. Далее давайте сосредоточим наше внимание на тех же самых функциях-обработчиках, которые мы имели для каждого из этих фрагментов состояния, установленных для захвата значений ввода, когда событие onChange срабатывает каждый раз, когда пользователь вводит новое значение в конкретное поле. Как вы можете видеть, они занимают довольно много места, поскольку мы устанавливаем это состояние для КАЖДОГО поля ввода. Представьте себе, если бы у нас была гораздо большая форма, принимающая гораздо больше информации от пользователя. Наш код был бы более исчерпывающим и повторяющимся. Давайте это исправим.

Первым делом мы создадим новую функцию, которая будет обрабатывать все события onChange для каждого поля ввода. Мы назовем ее handleChange. Следующая часть становится немного сложной, потому что наша функция-сеттер, setFormData, ожидает объект, поэтому нам нужно вызвать ее с объектом. Поэтому нам нужно использовать фигурные скобки, когда мы вызываем функцию-сеттер. Мы хотим, чтобы этот объект содержал все данные из переменной formData, поэтому мы будем использовать оператор spread для копирования всех исходных данных из переменной formData, которая, опять же, является всеми данными из объекта. Вторым аргументом при использовании оператора spread будет изменение, происходящее в этом поле ввода, ключ ввода, который является целевым или обновленным изменением, влияющим на состояние. Поэтому здесь мы включим e.target.value в качестве значения в пару ключ-значение.



Это уже выглядит намного красивее, но подождите, это только обновление имени пользователя. Нам нужно обновить другие поля, найдя способ использовать другие ключи в объекте. Нам нужен способ динамически определить, какую часть формы мы хотим обновить. К счастью для нас, входные данные на самом деле имеют присвоенные им идентификаторы, которые точно совпадают с именами полей ввода, на которые мы хотим нацелиться, они совпадают с ключами нашего объекта данных, что очень хорошо для нас! Возможно, это нужно иметь в виду при создании формы. Это также не обязательно означает, что вам нужно постоянно использовать id, вы также можете использовать name, если он есть или если вы создали его в каждой метке. Итак, начнем, давайте пока использовать идентификаторы.

Давайте создадим переменную в новой функции handleChange для указания каждого целевого поля по id. Это позволит определить, какой ключ в состоянии мы хотим обновить. Если мы хотим использовать переменную key динамически, чтобы определить, какой ключ в форме мы обновляем, мы можем написать ее, обернув в скобочную нотацию, и пусть значение этой пары ключей будет событием, захватывающим значение при изменении. Это будет выглядеть следующим образом:



Однако есть еще одна вещь, которая не совсем работает из-за всех наших изменений, и это последнее поле, наш специальный флажок. Он нуждается в особой логике, чтобы показывать true или false, когда он установлен или нет, вместо того, чтобы регистрироваться как просто on. Мы даже можем проверить это с помощью расширения Components в DevTools, проверив состояние true или false, которое мы указали в нашем объекте данных, когда мы нажимаем на флажок. Но нам все еще нужно придумать какой-то способ отличить, когда мы нажимаем на флажок, что этот тип ввода отделен от всех других вводов. Давайте добавим новую переменную для этого значения, чтобы каждый раз, когда тип целевого события — флажок, показывать checked, а если это не тип флажка, то возвращать значения других типов ввода. Вот где пригодится тернарный оператор для визуализации этой логики.



Прекрасно. Теперь самое интересное. Удалите все эти старые обработчики. Чистый дом, мой друг! А теперь обновите все старые обработчики onChange и замените их ОДНОЙ функцией handleChange, которую мы создали. Теперь будет проще добавить новое поле в нашу форму при необходимости, просто добавив его в объект данных и просто включив его в JSX с уже созданной логикой для отображения изменений пользователя.

Заключительный код


import React, { useState } from "react";

function Form() {

  const [formData, setFormData] = useState({
            username: "",
            password: "",
            avatar: "",
            mailingList: false
  })

    function handleChange(event) {
        const key = event.target.id
        const value = event.target.type === "checkbox" 
        ? event.target.checked : event.target.value

        setFormData({ 
          ...formData, 
          [key]: value
        })
      } 

  return (
    <form onSubmit={handleSubmit}>
      <h1>Create an Account</h1>
      <label htmlFor="username">Username</label>
      <input
        type="text"
        id="username"
        value={formData.username}
        onChange={handleChange}
      />

      <label htmlFor="password">Password</label>
      <input
        type="password"
        id="password"
        value={formData.password}
        onChange={handleChange}
      />

      <label htmlFor="avatar">Avatar Image</label>
      <input
        type="text"
        id="avatar"
        value={formData.avatar}
        onChange={handleChange}
      />
      <img src="https://i.pinimg.com/originals/0a/dd/87/0add874e1ea0676c4365b2dd7ddd32e3.jpg"
        alt="Avatar preview"
      />

      <label>
        Join our Mailing List!
        <input
          type="checkbox"
          id="mailingList"
          checked={formData.mailingList}
          onChange={handleChange}
        />

      </label>
      <input type="submit" value="Sign Up" />
    </form>

  )
}

export default Form
Вход в полноэкранный режим Выйти из полноэкранного режима

Лаконично, не так ли?

Бонус: Добавление почтового запроса

В каждой правильной форме мы хотели бы получить и где-то сохранить новые данные пользователя, верно? Что ж, наша форма готова принять POST-запрос. Нам просто нужно добавить его в функцию handleForm, чтобы любой новый пользовательский ввод был перехвачен и добавлен в нашу базу данных. Вот простой пример:


const handleForm = (e) => {
    e.preventDefault()

      fetch("http://localhost:3000", {
        method: "POST",
        headers: {
          "Content-Type" : "application/json"
         },
        body: JSON.stringify(formData)
        })
         .then(res => res.json())
         .then(data => setNewFormData(data))

      setFormData({
            username: "",
             password: "",
            avatar: "",
            mailingList: false
      })
    } 
  }
Вход в полноэкранный режим Выход из полноэкранного режима

Второе обещание будет включать функцию, которая будет обрабатывать новое состояние, принимая новые данные, которые мы добавляем в базу данных. Вы сами решите, где вы хотите разместить эту функцию и нужно ли передавать ее в качестве реквизита компоненту вашей формы, но пока это шаблонный POST-запрос, который вы можете добавить в вашу форму.

Если вы заметили, что поля ввода не сбрасываются после отправки новых данных, то обратите внимание на сброс состояния до начального состояния объекта, которое содержит пустые строки и значение false после завершения запроса.

А теперь отправляйтесь туда и творите волшебство, пробуя это самостоятельно! ✨

Ресурсы:
Визуальное представление

Оцените статью
devanswers.ru
Добавить комментарий