Reputation: 1878
I'm trying to create a form validation with react-hook-form
in my current project. I've already tried different approaches but always I got errors because of the ref
attribute. If I change the <FormField>
to input
, it starts to work.
Any idea how to solve this?
Contact
import React from 'react';
import { useForm } from "react-hook-form";
import FormField from '../../components/FormField';
import Button from '../../components/Button';
const Contact = () => {
const { handleSubmit, register, errors } = useForm();
const onSubmit = values => console.log(values);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<FormField
name="email"
onChange={() => { console.log("changed!") }}
ref={register({
required: "Required",
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: "invalid email address"
}
})}
/>
<p style={{ color: "red" }}>
{errors.email && errors.email.message}
</p>
<Button>Submit</Button>
</form>
);
};
export default Contact;
FormField
import React from "react";
import PropTypes from "prop-types";
import styled, { css } from "styled-components";
const FormFieldWrapper = styled.div`
position: relative;
textarea {
min-height: 150px;
}
input[type="color"] {
padding-left: 67px;
}
`;
const Label = styled.label``;
Label.Text = styled.span`
color: #e5e5e5;
height: 57px;
position: absolute;
top: 0;
left: 16px;
display: flex;
align-items: center;
transform-origin: 0% 0%;
font-size: 18px;
font-style: normal;
font-weight: 300;
transition: 0.1s ease-in-out;
`;
const Input = styled.input`
background: #53585d;
color: #f5f5f5;
display: block;
width: 100%;
height: 57px;
font-size: 18px;
outline: 0;
border: 0;
border-top: 4px solid transparent;
border-bottom: 4px solid #53585d;
padding: 16px 16px;
margin-bottom: 45px;
resize: none;
border-radius: 4px;
transition: border-color 0.3s;
&:focus {
border-bottom-color: var(--primary);
}
&:focus:not([type="color"]) + ${Label.Text} {
transform: scale(0.6) translateY(-10px);
}
${({ value }) => {
const hasValue = value.length > 0;
return (
hasValue &&
css`
&:not([type="color"]) + ${Label.Text} {
transform: scale(0.6) translateY(-10px);
}
`
);
}}
`;
function FormField({ label, type, name, value, onChange, ref }) {
const isTypeTextArea = type === "textarea";
const tag = isTypeTextArea ? "textarea" : "input";
return (
<FormFieldWrapper>
<Label>
<Input
as={tag}
type={type}
value={value}
name={name}
onChange={onChange}
ref={ref}
/>
<Label.Text>{label}:</Label.Text>
</Label>
</FormFieldWrapper>
);
}
FormField.defaultProps = {
type: "text",
value: "",
};
FormField.propTypes = {
label: PropTypes.string,
name: PropTypes.string.isRequired,
type: PropTypes.string,
value: PropTypes.string,
onChange: PropTypes.func,
ref: PropTypes.func
};
export default FormField;
Upvotes: 3
Views: 14611
Reputation: 1342
Referring to the docs the register should be used as below so we won't get refs issues and also the register will change the value inside the input so we don't need to pass a value prop :
Contact :
import React from "react";
import { useForm } from "react-hook-form";
import FormField from "../../components/FormField";
import Button from "../../components/Button";
const Contact = () => {
const { handleSubmit, register, errors } = useForm();
const onSubmit = (values) => console.log("values", values);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<FormField
name="email"
onChange={() => {
console.log("changed!");
}}
register={register}
/>
<p style={{ color: "red" }}>{errors.email && errors.email.message}</p>
<Button>Submit</Button>
</form>
);
};
export default Contact;
FormField :
import React from "react";
import PropTypes from "prop-types";
import styled, { css } from "styled-components";
const FormFieldWrapper = styled.div`
position: relative;
textarea {
min-height: 150px;
}
input[type="color"] {
padding-left: 67px;
}
`;
const Label = styled.label``;
Label.Text = styled.span`
color: #e5e5e5;
height: 57px;
position: absolute;
top: 0;
left: 16px;
display: flex;
align-items: center;
transform-origin: 0% 0%;
font-size: 18px;
font-style: normal;
font-weight: 300;
transition: 0.1s ease-in-out;
`;
const Input = styled.input`
background: #53585d;
color: #f5f5f5;
display: block;
width: 100%;
height: 57px;
font-size: 18px;
outline: 0;
border: 0;
border-top: 4px solid transparent;
border-bottom: 4px solid #53585d;
padding: 16px 16px;
margin-bottom: 45px;
resize: none;
border-radius: 4px;
transition: border-color 0.3s;
&:focus {
border-bottom-color: var(--primary);
}
&:focus:not([type="color"]) + ${Label.Text} {
transform: scale(0.6) translateY(-10px);
}
${({ value = {} }) => { // here you should find an other approch because there is no value props
const hasValue = value.length > 0;
return (
hasValue &&
css`
&:not([type="color"]) + ${Label.Text} {
transform: scale(0.6) translateY(-10px);
}
`
);
}}
`;
const FormField = ({ label, type, name, onChange, register }) => {
const isTypeTextArea = type === "textarea";
const tag = isTypeTextArea ? "textarea" : "input";
return (
<FormFieldWrapper>
<Label>
<Input
as={tag}
type={type}
// value={value} it's not a controlled input! so the register'ill provide the value
name={name}
onChange={onChange}
ref={register({
required: "Required",
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: "invalid email address",
},
})}
/>
<Label.Text>{label}:</Label.Text>
</Label>
</FormFieldWrapper>
);
};
FormField.defaultProps = {
type: "text",
value: "",
};
FormField.propTypes = {
label: PropTypes.string,
name: PropTypes.string.isRequired,
type: PropTypes.string,
value: PropTypes.string,
onChange: PropTypes.func,
ref: PropTypes.func,
};
export default FormField;
Upvotes: 5