Нужна помощь в реализации react-hook-form с Gatsby.js : форма даже не будет отправлять значения в консоль

#javascript #reactjs #react-hook-form

#javascript #reactjs #react-hook-form

Вопрос:

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

Тем не менее, это было не так уж и здорово (я интерфейсный парень, пытающийся сделать все возможное здесь), и после некоторых исследований я узнал о react-hook-form, который мог не только обрабатывать эту логику компонентов (с formState .коснитесь и formState.IsDirty ), но сделайте проверки для меня. Звучало как план.

Итак, я удалил эти компоненты их логики, чтобы они были проще, и пытаюсь использовать их в этой форме. Но я даже не могу отправлять значения полей на консоль, что означает, что я использую неправильный подход. Я считаю, что я неправильно регистрирую компоненты, перепробовав уже много способов сделать это.

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

ОБНОВЛЕНИЕ: после небольшого изучения мне удалось заставить его работать частично — по крайней мере, для ввода текста. Я отвечу на этот вопрос, чтобы я мог сделать еще один, касающийся оставшейся проблемы.

Вот код, который у меня есть сейчас для компонентов

Input.js

 // Input.js - from where <Input> and <TextArea> components come from
import React from "react";

import "../../sass/components/forms/input.sass"

export function Input({ className, label, name, onChange, value, register, required, type }) {
  
  return (
    <div 
      className={`inputContainer ${className}`}>
      <div
        className={`
        inputHeader 
        `}>
        <span className="inputLabel" >{label}</span>
        <input 
          id={name}
          className="field"
          onChange={onChange} 
          value={value}
          ref={register({ required })}
          type={type}/>
      </div>
    </div>
  )
}
  

export function TextArea({ className, label, name, onChange, value, register, required, type }) {

  return (
    <div 
      className={`
      textAreaContainer ${className}
      `}>
      <span className="textAreaLabel">{label}</span>
      <textarea 
        id={name}
        className="field" 
        onChange={onChange} 
        ref={register({ required })}
        type={type} />
    </div>
  )
}
  

Dropdown.js

 // Dropdown.js - from where <Dropdown> comes. Notice that it's not a <select> tag but a list with containers around. That was the only way I was able to customize it the way we need it.

import React, { useState } from "react";
import "../../sass/components/forms/dropdown.sass"


export function Dropdown(props) {
  const [isOpen, setIsOpen] = useState(false);
  const [selectedOption, setSelectedOption] = useState(null);
    
  const toggling = () => setIsOpen(!isOpen);

  const onOptionClicked = value => () => {
    setSelectedOption(value);
    setIsOpen(false);
  };

  return (
    <div className="dropdownContainer">
      <span>{props.preambulo}</span>
      <div 
        className={`
          dropdownHeader 
          ${isOpen === true ? 'open' : 'closed'} 
          `} 
          onClick={toggling}
          >
        {selectedOption || props.placeholder}
      </div>
      {isOpen amp;amp; (
        <div 
          className="dropdownListContainer">
            <ul 
              className="dropdownList"
              ref={props.ref}
              name={props.name}
              id={props.name}
              >
              {props.options.map((option, i) => (
                <li 
                  className={`dropdownListItem item-${i}`} 
                  key={i} 
                  onClick={onOptionClicked(option)}>
                  {option}
                </li>
              ))}
              <hr />
            </ul>
        </div>
        )}
    </div>
  );
}
  

А затем форма.

contatoFull.js

 import React from "react";
import { useForm } from "react-hook-form";

//Form Components
import { Dropdown } from "../../components/forms/Dropdown"
import { Input, TextArea } from "../../components/forms/Input"

const ContatoFull = ({ className }) => {
 
  const cargos = [
    "Chefe",
    "Diretor",
    "Gerente",
    "Funcionário",
    "Outros"
  ]

  const estados = [
    "Acre",
    "Alagoas",
    "Amazonas",
    "Amapá",
    "Bahia",
    "Ceará",
    "Distrito Federal",
    "Espírito Santo",
    "Goiás",
    "Maranhão",
    "Minas Gerais",
    "Mato Grosso do Sul",
    "Mato Grosso",
    "Pará",
    "Paraíba",
    "Pernambuco",
    "Piauí",
    "Paraná",
    "Rio de Janeiro",
    "Rio Grande do Norte",
    "Rondônia",
    "Roraima",
    "Rio Grande do Sul",
    "Santa Catarina",
    "Sergipe",
    "São Paulo",
    "Tocantins"
  ]
  const { register, handleSubmit } = useForm();
  const onSubmit = data => console.log(data);

  return (
    <form 
      method="post"
      className={`${className !== 0 ? className : ''}`} 
      onSubmit={handleSubmit(onSubmit)}>

      <Dropdown 
        options={cargos}
        ref={register} 
        className="cargo dropdown" 
        preambulo="Eu sou" 
        placeholder="Tomador(a) de decisão" />

      <Dropdown 
        options={estados}
        ref={register} 
        className="estado dropdown" 
        preambulo="Estou no" 
        placeholder="Acre" />

      <Input 
        register={register}
        required
        className="empresa" 
        label="Nome da minha empresa" 
        type="text " />
        
      
      <Input 
        register={register}
        required
        className="primeiro nome" 
        label="Nome" 
        type="text" />
      <Input 
        register={register}
        required
        className="segundo nome" 
        label="Sobrenome" 
        type="text" />
      <Input 
        register={register({
          required: true, 
          pattern: /^S @S $/i
        })}
        required
        className="email" 
        label="E-mail de contato" 
        type="email" 
        />
        
      <Input
        register={register({
          required: true, minLength: 6, maxLength: 12
        })}
        required
        className="telefone" 
        label="Telefone de contato" 
        type="tel" />

      <TextArea
        register={register}
        required
        className="mensagem" 
        label="Escreva sua mensagem"/>

      <button 
        type="submit" 
        className="simpleButton primary submit button">Enviar</button>
    </form>
  )
}

export default ContatoFull
  

Ответ №1:

Проблема с кодом в этом вопросе заключается в регистрации этих полей, <Input> , <TextArea> и <Dropdown> . Способ, который я использовал, передавая реквизиты register компонентам, был неправильным.

Использование <Controller> для правильной регистрации полей после некоторой работы. Вот что я сделал:

 import { useForm, Controler } from 'react-hook-form' 

const ContatoForm = () => {

  (...)

  const { handleSubmit, control } = useForm();
  const onSubmit = data => console.log(data);

  return (
    <form 
      method="post"
      onSubmit={handleSubmit(onSubmit)}>
      <Controller
        control={control}
        name="firstName"
        render={props =>
          <Input
            name="firstName"
            type="text"
            label="label here"
            value={value}
            onChange={e => onChange(e.target.value)}
        }
      />
    </form>
  )
}
  

Код для текстовых компонентов <input> был в основном оболочкой inputContainer для inputHeader , inputLabel и сам ввод field :

 //** Component structure
   <div 
      className={`inputContainer ${className}`}>
      <div
        className={`
        inputHeader 
        `}>
        <span className="inputLabel" >{label}</span>
        <input 
          id={name}
          className="field"
          onChange={onChange} 
          value={value}
          ref={register({ required })}
          type={type}/>
      </div>
    </div>
  

Однако, например, применение проверки для обязательных полей приведет к тому, что ошибки окажутся за пределами этой структуры. Что-то вроде этого:

       <Controller
        control={control}
        name="firstName"
        render={props =>
          <Input
            name="firstName"
            type="text"
            label="label here"
            value={value}
            onChange={e => onChange(e.target.value)}
        }
      />
      {errors.firstName amp;amp; <span>Field is required</span>}

  

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

 // New input component
export function Input({ label, name, onChange, value, type }) {
  
  return (
    <div
      className={`
      inputHeader 
      `}>
      <span className="inputLabel" >{label}</span>
      <input 
        id={name}
        className="field"
        onChange={onChange} 
        value={value}
        type={type}/>
    </div>
  )
}
  
 // New parent 
(...)
  const { handleSubmit, control, errors } = useForm();
  const onSubmit = data => console.log(data);
  return (
    <div 
      className={`inputContainer firstName`}>
      <Controller 
        name="firstName" 
        label="Name" 
        type="text"
        control={control}
        defaultValue=""
        rules={{ required: true }}
        render={({ onChange, value, name, type, label }) =>
          <Input 
            onChange={e => onChange(e.target.value)}
            value={value}
            label={label} 
            name={name}
            type={type} />
        }
      />
      {errors.firstName amp;amp; <span className="error">Field is required.</span>}
   )

  

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