react-hooks-form conflict with reactstrap - reactjs

I'm trying to add react-hook-form to my reactstrap form and inputs, but looks like it has a conflict between the packages.
import React, { useState, useEffect } from 'react';
import { useForm } from "react-hook-form";
import {
Button,
Form,
Label,
Input
} from 'reactstrap';
import { FormControlLabel, Checkbox, FormGroup } from '#material-ui/core';
...
const { register, handleSubmit, errors } = useForm();
...
const updDetails = (data) => {
console.log(data);
}
return (
<Form onSubmit={handleSubmit(updDetails)}>
<FormGroup className="mr-10 mb-10">
<Label for="compName" className="mr-sm-10">Company Name</Label>
<Input type="text" name="compName" id="compName" placeholder="Company Name"
ref={register({ required: true })} aria-invalid={errors.name ? "true" : "false"}
value={compDetails.compName} onChange={(e) => setCompDetails({...compDetails, compName: e.target.value})}
/>
{errors.name && errors.name.type === "required" && (
<span role="alert">This is required</span>
)}
</FormGroup>
<Button type="submit" className="col-sm-3" color="primary">Update Details</Button>
</Form>
)
...
When I load the page, I can see in the browser console warnings for all my inputs: Field is missing `name` attribute . However, I am adding the name to every input as in the code above. Also, when I click on Update Details with an empty value for the compName it submits and the data is empty object.
Can't I use react-hook-form with reactstrap?
Thanks

You probably have to use innerRef instead of ref on the Input component. i found this in the source here.
<Input
type="text"
name="compName"
id="compName"
placeholder="Company Name"
innerRef={register({ required: true })}
aria-invalid={errors.compName ? "true" : "false"}
/>

I am not familiar with the reactstrap code, but a thing you could do is pass the name to the register function, use it this way:
ref={register({name: 'input-name', /* rest of options*/})}
Hope I could help :D

Related

The react-hook-form library and React Ref

Having such a simple react-hook-form example form:
import React from 'react';
import { useForm } from 'react-hook-form';
export default function ReactForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
const onSubmit = (data) => console.log(data);
let textInput = null;
function handleClick() {
textInput.focus();
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label htmlFor="firstName">First Name:</label>
<input
id="firstName"
ref={(input) => {
console.log("firstName ref...")
textInput = input
}
}
{...register('firstName')} />
<br/>
<label htmlFor="lastName">Last Name:</label>
<input id="lastName" { ...register('lastName', { required: true })} /><br/>
<label htmlFor="age">Age:</label>
<input id="age" {...register('age', { pattern: /\d+/ })} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/><br/>
<input type="submit" />
{errors.firstName && <p>First name is required!</p>}
{errors.lastName && <p>Last name is required!</p>}
{errors.age && <p>Please enter number for age!</p>}
</form>
);
}
I'm getting :
Cannot read properties of null (reading 'focus')
error. The reason for this is that the ref seems not to be called at all (NOT giving the the firstName ref... in the console). Why doesn't the ref NOT being called & working!?
P.S.
I'he rewritten the above WITHOUT using the react-hook-form and the ref do work as expected so the problem lies somewhere in the react-hook-form library!
Your ref prop is being overridden by react-hook-form's register. The result of register includes it's own ref property that you're spreading onto the input. The docs indicate this is used for focusing the field on validation errors.
Your exception occurs in your click handler because your ref callback was never executed (and textInput is still null).
Try swapping the order so you override the ref provided by register('firstName') like so:
<input
id="firstName"
{...register('firstName')} // spread result of register including ref prop
ref={(input) => { // override ref prop
console.log("firstName ref...")
textInput = input
}}
/>

Yup errors don't fire before onBlur

I want this input field's Yup errors to fire as soon as a key is logged for maximum interactivity. However, I'm only getting the errors once I click out of the text field. After the first onBlur event happens and I click back the text field, the next errors are showed in real time.
How can I alter this behavior? I want an error to appear immediately if my first character is not a letter or if my name is not 5-12 characters long.
This is my code:
import ReactDOM from 'react-dom';
import { useFormik } from 'formik'
import * as Yup from 'yup';
function Container(){
const formik = useFormik({
initialValues: {
prodName: ''
},
validationSchema: Yup.object(
{
prodName : Yup.string().required('This field is required')
.matches(/^[aA-zZ\s]+$/, "Only letters are allowed for this field ")
.min(5,'Minimum is 5').max(12, 'No more than 12')
}
)
})
return(
<div id='prd-contain'>
<form onSubmit={formik.handleSubmit}>
<label className='prd-labels'>Product name</label>
<input type="text" id="prodName" name="prodName" value={formik.values.prodName}
onChange={formik.handleChange} onBlur={formik.handleBlur}/>
{formik.touched.prodName && formik.errors.prodName ? (<div>{formik.errors.prodName}</div>) : null}
<input type="submit" value="Submit"/>
</form>
</div>
)
}
ReactDOM.render(
<Container />,
document.getElementById('root')
);
You can simply change
<input type="text" id="prodName" name="prodName" value={formik.values.prodName} onChange={formik.handleChange} onBlur={formik.handleBlur}/>
to
<input type="text" id="prodName" name="prodName" value={formik.values.prodName} onChange={formik.handleChange} onkeyup={formik.handleBlur}/>

react-hook-form when editing doesn't update

I can't find a way to provide the existing information from the DB to the react-hook-form library to revalidate when clicking submits and to update it if the data was changed.
The problem is when clicking on submit button, this object will shown without any value for firstName and lastname:
{firstName: undefined, lastname: undefined}
Simplified version of my component:
import React from "react";
import { useForm } from "react-hook-form";
import { TextField } from "#material-ui/core";
const App = (props) => {
const { register, handleSubmit} = useForm();
const onSubmit = (data) => {
console.log(data); // ---> here
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<h2>Step 1</h2>
<label>
First Name:
<TextField {...register("firstName")} defaultValue="firstname" />
</label>
<label>
Last Name:
<TextField {...register("lastname")} defaultValue="lastname" />
</label>
<input type="submit" />
</form>
);
};
export default App
Any idea how to create a form that allows you to edit the fields?
Check out on codesandbox
The problem is with the TextFiled component. the register is not set correctly.
According to the MUI documentation:
props:inputProps type:object description:Attributes applied to the input element.
So you need to pass the register throw the inputProps:
<TextField inputProps={register("firstName")} defaultValue="firstname" />
<TextField inputProps={register("lastname")} defaultValue="lastname" />

Why defaultValue from React hook form can not work in react

I stuck in a problem. I am using React Hook Form
in my shipment component everything has gone well but while i try to use react hook form for my shipment component and setting default value on these component it's not work accurately. My code is given below:
import React, { useContext } from 'react';
import { useForm } from 'react-hook-form';
import { UserContext } from '../../App';
import './Shipment.css';
const Shipment = () => {
const { register, handleSubmit, watch, formState: { errors } } =
useForm();
const [loggedInUser,setLoggedInUser] = useContext(UserContext);
const onSubmit = data => {
console.log('form submitted',data);}
console.log(watch("example")); // watch input value by passing the name
of it
return (
<form className="ship-form" onSubmit={handleSubmit(onSubmit)}>
{/* <input defaultValue={loggedInUser.email}
{...register("example")} /> */}
<input name="name" defaultValue={loggedInUser.name}
{...register("exampleRequired", { required: true })} placeholder="Your
Name" />
{errors.name && <span className="error">Name is required</span>}
<input name="email" defaultValue={loggedInUser.email}
{...register("exampleRequired", { required: true })}
placeholder="Email address"/>
{errors.email && <span className="error">Email is required</span>}
<input name="address" {...register("exampleRequired", { required:
true })} placeholder="address"/>
{errors.address && <span className="error">Address is
required</span>}
<input name="phone" {...register("exampleRequired", { required: true
})} placeholder="Phone number"/>
{errors.phone && <span className="error">Phone Number is
required</span>}
<input type="submit" />
</form>
);
};
export default Shipment;
when i attempt to sign in by using email :
i set default value on name and email field but it's not work except name
and my console show only name except all data
Actually i need all data and set default value on a specific field
I think the problem is here:
Before
<input name="email" defaultValue={loggedInUser.email}
{...register("exampleRequired", { required: true })}
placeholder="Email address"/>
After:
<input defaultValue={loggedInUser.email}
{...register("email", { required: true })}
placeholder="Email address"/>
All of your register have the same name, that's why it doesn't work.
See the register API

Conditional validation with react hook form

Here is my form looks like and also CodeSanbox. currently I'm using react-hook-form
as you can see form has 3 inputs. Submit button should be disabled until all the required fields are entered.
Two use case:
If "Check" is unchecked:
only "id" should be validated and submit button should get enabled. "firt" and "last" names should not be part of form data
If "Check" is checked
all the fields should be validated
first and last names are only required if "Check" is checked. so its not checked then form should only validate "ID" field. if "Check" is checked then all fields should get validated.
problem I'm having is if I enter id, form state is still "invalid". Form is expecting to enter values for first and last name.
I would appreciate any help.
I have updated your CodeSanBox code and also adding the full code here:
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import { useForm } from "react-hook-form";
import "./index.css";
function App() {
const {
register,
handleSubmit,
errors,
formState,
unregister,
setValue,
getValues,
reset
} = useForm({
mode: "onBlur",
reValidateMode: "onBlur",
shouldUnregister: true
});
//console.log(formState.isValid);
console.log(errors);
const [disabled, setDisabled] = useState(true);
const onSubmit = (data) => {
alert(JSON.stringify(data));
};
useEffect(() => {
// #ts-ignore
if (disabled) {
console.log("unregister");
reset({ ...getValues(), firstName: undefined, lastName: undefined });
unregister(["firstName", "lastName"]);
} else {
console.log("register");
register("firstName", { required: true });
register("lastName", { required: true });
}
}, [disabled]);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label htmlFor="id">ID</label>
<input
name="id"
placeholder="id"
ref={register({ required: true, maxLength: 50 })}
/>
{errors.id && <p>"ID is required"</p>}
<fieldset disabled={disabled}>
<legend>
<input
type="checkbox"
name={"name"}
ref={register}
onClick={() => setDisabled(!disabled)}
/>
<span>Check</span>
</legend>
<label htmlFor="firstName">First Name</label>
<input
name="firstName"
placeholder="Bill"
onChange={(e) => {
console.log(e.target.value);
setValue("firstName", e.target.value);
}}
ref={register({ required: !disabled })}
/>
{errors.firstName && <p>"First name is required"</p>}
<label htmlFor="lastName">Last Name</label>
<input
name="lastName"
placeholder="Luo"
onChange={(e) => setValue("lastName", e.target.value)}
ref={register({ required: !disabled })}
/>
{errors.lastName && <p>"Last name is required"</p>}
</fieldset>
<input type="submit" disabled={!formState.isValid} />
</form>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
First I found that you set disabled state as false which should be true as an initial value, and regarding the issue, I have used reset and getValues functions when the disabled state changes.
EDIT for you to recognize code changes easy, I have restored all the code at CodeSanBox.
This whole validation behavior (UX) is definitely making things a bit harder, however, there are a couple of things that you should leverage from the library such as:
watch
validate
getValues
import React from "react";
import ReactDOM from "react-dom";
import { useForm } from "react-hook-form";
import "./index.css";
function App() {
const {
register,
handleSubmit,
errors,
formState: { isValid, touched },
getValues,
trigger,
watch
} = useForm({
mode: "onBlur"
});
const onSubmit = (data) => {
alert(JSON.stringify(data));
};
const validate = (value) => {
if (getValues("name")) { // read the checkbox value
return !!value;
}
return true;
};
const isChecked = watch("name"); // watch if the name is checked
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label htmlFor="id">ID</label>
<input
name="id"
placeholder="id"
ref={register({ required: true, maxLength: 50 })}
/>
{errors.id && <p>"ID is required"</p>}
<fieldset disabled={!isChecked}>
<legend>
<input
type="checkbox"
name={"name"}
ref={register}
onChange={() => trigger()} // you want update isValid due to state change, and also those extra two inputs become required
/>
<span>Check</span>
</legend>
<label htmlFor="firstName">First Name</label>
<input
name="firstName"
placeholder="Bill"
ref={register({
validate
})}
/>
// make sure input is touched before fire an error message to the user
{errors.firstName && touched["firstName"] && (
<p>"First name is required"</p>
)}
<label htmlFor="lastName">Last Name</label>
<input
name="lastName"
placeholder="Luo"
ref={register({
validate
})}
/>
{errors.lastName && touched["lastName"] && (
<p>"Last name is required"</p>
)}
</fieldset>
<input type="submit" disabled={!isValid} />
</form>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
CSB:
https://codesandbox.io/s/react-hook-form-conditional-fields-forked-n0jig?file=/src/index.js:0-1831
on your ref, dont use hard coded bool true, ref={register({ required: true})}, but your dynamic ref={register({ required: disabled })}
do notice that because your mode: "onBlur" config, the button won't be abled until id field blurred
You just need to replace true .from ref: required:true..... Instead use const 'disabled' ....in input of first and last name .
So as to achieve dynamic change

Resources