#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>}
)
Таким образом, я могу получить входное значение при отправке с надлежащей проверкой.