Форма является очень распространенным элементом веб-сайтов, так как является одним из способов взаимодействия между пользователем и веб-сайтом.

Форма позволяет пользователю вводить данные, которые могут быть отправлены на сервер или обработаны на стороне клиента и сохранены в локальном хранилище.

Форма может содержать несколько элементов —

  1. Виджет формы (оборачивает элементы формы)
  2. Элементы выравнивания — как ‹ul› и ‹li›
  3. Поля ввода и элементы textarea
  4. Флажки и раскрывающиеся списки

Элемент <form>

элемент формы искажает структуру формы. У него есть несколько возможных атрибутов, наиболее распространенными из которых являются:

  1. Действие — определяет место (URL), куда должны отправляться собранные данные формы при ее отправке. ПРИМЕЧАНИЕ. Чтобы отключить это поведение, вы можете использовать e.preventDefault() в обработчике отправки.
  2. Метод — ПОЛУЧИТЬ/ПОСТАВИТЬ
  3. Класс — для имени класса CSS
  4. Обработчики — как onSubmit

Элементы <input> и <textarea>

Input — это элемент для заполнения данных или для загрузки файлов. Он имеет множество атрибутов, которые помогают описать пользователю желаемый ввод. Наиболее полезными атрибутами являются «тип» и обработчики (onChange, onBlur), которые определяют, как значение отображается и ведет себя. Что меня действительно поразило в элементе ввода, так это то, что он имеет встроенное состояние.

Элемент input предназначен для однострочного ввода, а textarea — для нескольких строк.

function App() {
  const [num, setNum] = useState(0)

  useEffect(() => {
    setInterval(() => {
      setNum(num + 1)
    }, 2000)
  }, [num])

  const handleClick = () => {
    alert('click!')
  }
  return (
      <div className="App">
        <form>
          <ul>
            <li>
              <label for="name">Name:</label>
              <input type="text" id="name" name="user_name" />
            </li>
            <li>
              <label for="mail">E-mail:</label>
              <input type="email" id="mail" name="user_email" />
            </li>
            <li>
              <label>{num}</label>
            </li>
            <li>
              <button onClick={handleClick}>click</button>
            </li>
          </ul>
        </form>
      </div>
  )
}

В приведенном выше примере у нас есть setInterval, который запускается каждые 2 секунды и увеличивает переменную num. Независимо от того, сколько рендеров, значения в элементе ввода останутся и не изменятся. Если вы введете «мое имя» в элемент ввода, оно не изменится даже после рендеринга.

Элемент <button>

Кнопка имеет решающее значение для отправки данных. Это зависит от вашего решения — его можно отключить до тех пор, пока не будут заполнены все обязательные поля, а можно включить постоянно. Наиболее полезными атрибутами являются type, disabled и onClick.

Атрибут type определяет поведение кнопки — это может быть «отправить» / «сбросить» / «кнопка».

Формы могут быть контролируемыми или неконтролируемыми

Элементы формы, такие как ‹input›, ‹textarea› и ‹select›, имеют встроенное состояние. Вы можете использовать их состояние (сохраненное в DOM) или состояние компонента React.

Управляемый компонент — это компонент, который управляет состоянием формы и сохраняет его с помощью useState.

function App() {
  const [num, setNum] = useState(0)
  const [userNameValue, setUserNameValue] = useState('')
  const [list, setList] = useState([])

  useEffect(() => {
    setInterval(() => {
      setNum(num + 1)
    }, 700000)
  }, [num])

  const handleChange = (e) => {
    setUserNameValue(e.target.value)
  }

  const handleSubmit = (e) => {
    e.preventDefault() 
    /* const formEl = document.forms.BookPackageForm
    const formData = new FormData(formEl)
    const newList = list.concat(formData.get('user_name'))*/
    const newList = list.concat(userNameValue)
    setList(newList)
    setUserNameValue('')
  }

  return (
      <div className="App">
        <form action="" onSubmit={handleSubmit} id="BookPackageForm">
          <ul>
            <li>
              <label for="name">Name:</label>
              <input
                type="text"
                id="name"
                name="user_name"
                value={userNameValue}
                onChange={handleChange}
              />
            </li>
            <li>
              <label for="mail">E-mail:</label>
              <input type="email" id="mail" name="user_email" />
            </li>
            <li>
              <label>{num}</label>
            </li>
            <li>
              <button type="submit" disabled={!!!userNameValue}>
                submit
              </button>
            </li>
            <li>
              <ul>
                {list.map((item) => (
                  <li key={item}>{item}</li>
                ))}
              </ul>
            </li>
          </ul>
        </form>        
      </div>
  )
}

export default App

Элементы ввода в контролируемом компоненте:

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

Атрибуту элемента ввода value присвоено значение userNameValue (часть состояния компонента). Это делает состояние React источником правды. handleChange запускается при каждом нажатии клавиши для обновления userNameValue в состоянии React. Нам обещают, что отображаемое значение будет обновляться по мере ввода пользователем.

С управляемым компонентом значение ввода всегда определяется состоянием React. Хотя это означает, что вам придется ввести немного больше кода, теперь вы также можете передавать значение другим элементам пользовательского интерфейса или сбрасывать его из других обработчиков событий.

Обратите внимание, что ‹input type="file" /› нельзя контролировать, так как его значение доступно только для чтения.

Выберите элементы в контролируемом компоненте:

В раскрывающихся списках выбран атрибут для подписи выбранного значения.

В управляемом компоненте мы его использовать не будем, вместо него мы устанавливаем выбранное значение в состоянии компонента и передаем его в выпадающий список через атрибут value.

function App() {
  const [num, setNum] = useState(0)
  const [userNameValue, setUserNameValue] = useState('')
  const [cityValue, setCityValue] = useState('paris')
  const [list, setList] = useState([])

  useEffect(() => {
    setInterval(() => {
      setNum(num + 1)
    }, 700000)
  }, [num])

  const handleChange = (e) => {
    setUserNameValue(e.target.value)
  }

  const handleCityChange = (e) => {
    setCityValue(e.target.value)
  }

  const handleSubmit = (e) => {
    e.preventDefault()
    const newList = list.concat(userNameValue)
    setList(newList)

    setUserNameValue('')
  }

  return (
      <div className="App">
        <form action="" onSubmit={handleSubmit} id="BookPackageForm">
          <ul>
            <li>
              <label for="name">Name:</label>
              <input
                type="text"
                id="name"
                name="user_name"
                value={userNameValue}
                onChange={handleChange}
              />
            </li>
            <li>
              <label for="mail">E-mail:</label>
              <input type="email" id="mail" name="user_email" />
            </li>
            <li>
              <label for="city">City:</label>
              <select value={cityValue} onChange={handleCityChange}>
                <option value="tlv">Tel Aviv</option>
                <option value="london">London</option>
                <option value="paris">Paris</option> 
                <option value="ny">New York</option>
              </select>
            </li>
            <li>
              <label>{num}</label>
            </li>
            <li>
              <button type="submit" disabled={!!!userNameValue}>
                click
              </button>
            </li>
            <li>
              <ul>
                {list.map((item) => (
                  <li key={item}>{item}</li>
                ))}
              </ul>
            </li>
          </ul>
        </form>
      </div>
  )
}

export default App

Неконтролируемые компоненты

Если вы решите реализовать неуправляемый компонент, данные будут контролироваться DOM. Для запроса вы должны использовать useRef, который передается элементу формы через атрибут ref. Неконтролируемый компонент хранит источник правды в DOM.

function App() {
  const [num, setNum] = useState(0)
  const [userNameValue, setUserNameValue] = useState('')
  const [cityValue, setCityValue] = useState('paris')
  const [list, setList] = useState([])
  const emailRef = useRef('')

  useEffect(() => {
    setInterval(() => {
      setNum(num + 1)
    }, 70000)
  }, [num])

  const handleChange = (e) => {
    setUserNameValue(e.target.value)
  }

  const handleCityChange = (e) => {
    setCityValue(e.target.value)
  }

  const handleSubmit = (e) => {
    e.preventDefault()
    const newList = list.concat(userNameValue) // why not push?
    setList(newList)

    setUserNameValue('')

    alert(emailRef.current.value)
  }

  return (
      <div className="App">
        <form action="" onSubmit={handleSubmit} id="BookPackageForm">
          <ul>
            <li>
              <label for="name">Name:</label>
              <input
                type="text"
                id="name"
                name="user_name"
                value={userNameValue}
                onChange={handleChange}
              />
            </li>
            <li>
              <label for="mail">E-mail:</label>
              <input type="email" id="mail" name="user_email" ref={emailRef} />
            </li>
            <li>
              <label for="city">City:</label>
              <select value={cityValue} onChange={handleCityChange}>
                <option value="tlv">Tel Aviv</option>
                <option value="london">London</option>
                <option value="paris">Paris</option>
                <option value="ny">New York</option>
              </select>
            </li>
            <li>
              <label>{num}</label>
            </li>
            <li>
              <button type="submit" disabled={!!!userNameValue}>
                click
              </button>
            </li>
            <li>
              <ul>
                {list.map((item) => (
                  <li key={item}>{item}</li>
                ))}
              </ul>
            </li>
          </ul>
        </form>
      </div>
  )
}

export default App

В приведенном выше примере видно, что у элемента textarea электронной почты нет атрибута value, вместо него — атрибут ref.

Если вы добавите атрибут avalue к элементам формы, он переопределит значение в DOM. Если вы хотите указать начальное значение, но оставить обновления для обработки DOM, вы можете указать атрибут defaultValue вместо value. Даже изменение значения атрибута defaultValue после монтирования компонента не приведет к обновлению значения в DOM.