Why defaultValue from React hook form can not work in react - reactjs

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

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
}}
/>

Simple react form validation with yup

I'm trying to learn form validation with yup and react-hook-form and, ideally want to set the validation to also require at least one of the two different-named radio options, but first understand why the isValid variable is false.
Here's my code:
import React from "react";
import { useForm } from "react-hook-form";
import * as yup from "yup"
const App = () => {
const { register, handleSubmit, watch, errors } = useForm();
const onSubmit = async data => {
console.log('form data', data);
const isValid = await schema.isValid();
console.log('isValid', isValid);
}
return (
<>
{console.log('errors', errors)}
<form onSubmit={handleSubmit(onSubmit)}>
<input name="name" defaultValue="test" ref={register} />
<input name="nameTwo" ref={register({ required: true })} />
{errors.exampleRequired && <span>This field is required</span>}
<label>
<input
name="test"
type="radio"
ref={register}
value="test"
/>
</label>
<label>
<input
name="testTwo"
type="radio"
ref={register}
value="testTwo"
/>
</label>
<input type="submit" />
</form>
</>
);
}
const schema = yup.object().shape({
name: yup.string(),
nameTwo: yup.string().required(),
test: yup.string(),
testTwo: yup.string()
})
export default App
So the schema defines only one field (nameTwo) as required, but even when filling in that field it still is false. Why is isValid not true? And how is it best to go about making either one of the two radio buttons (test or testTwo) required?
Stackblitz demo: https://stackblitz.com/edit/react-93vahd
Thank you
The isValid property is available in the formState object returned from the useForm hook.
You'll notice that onSubmit is not called unless the Yup validation passes. Therefore isValid will always be true when checking inside the onSubmit callback.
import React from 'react';
import { useForm } from 'react-hook-form';
import * as yup from 'yup';
const App = () => {
const {
register,
handleSubmit,
errors,
formState: { isValid }
} = useForm();
const onSubmit = async data => {
console.log('form data', data);
};
return (
<>
{console.log('isValid', isValid)}
{console.log('errors', errors)}
<form onSubmit={handleSubmit(onSubmit)}>
<input name="name" defaultValue="test" ref={register} />
<input name="nameTwo" ref={register({ required: true })} />
{errors.exampleRequired && <span>This field is required</span>}
<label>
<input name="test" type="radio" ref={register} value="test" />
</label>
<label>
<input name="testTwo" type="radio" ref={register} value="testTwo" />
</label>
<input type="submit" />
</form>
</>
);
};
const schema = yup.object().shape({
name: yup.string(),
nameTwo: yup.string().required(),
test: yup.string(),
testTwo: yup.string()
});
export default App;

react-hooks-form conflict with reactstrap

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

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

how to provide validations for react js form

How to provide validations for react js form.
I have taken form with two fields. if two fields are enabled then only need to enable save button.
import React from 'react'
import { Button, Checkbox, Form } from 'semantic-ui-react'
const FormExampleForm = () => (
<Form>
<Form.Field>
<label>First Name</label>
<input placeholder='First Name' />
</Form.Field>
<Form.Field>
<label>Last Name</label>
<input placeholder='Last Name' />
</Form.Field>
<Form.Field>
<Checkbox label='I agree to the Terms and Conditions' />
</Form.Field>
<Button type='submit'>Submit</Button>
</Form>
)
export default FormExampleForm
In redux forms 6.7 there's a simple property called pristine which can be used to do this. But if the user enters some value in at least one input field the submit button gets activated. To achieve that you may need to do something like this.
render() {
// const { handleSubmit } = this.props;
const {handleSubmit, pristine, reset, submitting} = this.props
return (
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<div>
<label>First Name</label>
<div>
<Field name="firstName" component="input" type="text" placeholder="First Name"/>
</div>
</div>
<button type="submit" className="btn btn-primary" disabled={pristine || submitting}>Submit</button>
</form>
);
}
}
But if you need to enable submit button, say when the user inputs values in 2 given fields or so, then this becomes complex. You need to do something like this. Here's my sample usecase. The form has 3 fields firstName, lastName and age. The submit button gets activated only if the user enters values for firstName and lastName input fields here. Please check out the following code.
import React, { Component } from 'react';
import { Field, reduxForm, formValueSelector } from 'redux-form';
import { connect } from 'react-redux';
class UserNew extends Component {
constructor(props) {
super(props);
this.isSubmitEnabled = this.isSubmitEnabled.bind(this);
}
onSubmit(values) {
console.log(values);
}
isSubmitEnabled() {
// Access field values here and validate them
const firstName = this.props.firstNameValue;
const lastName = this.props.lastNameValue;
if(firstName && lastName){
return true;
}
return false;
}
render() {
const { handleSubmit } = this.props;
const isEnabled = this.isSubmitEnabled();
return (
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<div>
<label>First Name</label>
<div>
<Field name="firstName" component="input" type="text" />
</div>
</div>
<div>
<label>Last Name</label>
<div>
<Field name="lastName" component="input" type="text" />
</div>
</div>
<div>
<label>Age</label>
<div>
<Field name="age" component="input" type="text" />
</div>
</div>
<button type="submit" className="btn btn-primary" disabled={!isEnabled}>Submit</button>
</form>
);
}
}
UserNew = reduxForm({
form: 'UserNewForm'
})(
UserNew
);
// Decorate with connect to read form values
const selector = formValueSelector('UserNewForm') // <-- same as form name
UserNew = connect(state => {
const firstNameValue = selector(state, 'firstName')
const lastNameValue = selector(state, 'lastName')
return {
firstNameValue,
lastNameValue,
}
})(UserNew)
export default UserNew;
To access field values you need to use formValueSelector in ReduxForms. For that you need to connect your form to redux store using connect helper.
Hope this helps. Happy coding !
Either you can use Redux-form as its not used in above code, so you can write custom JS validation code. For this you need to follow below
Initialise state in
contructor(){
this.state{
first_name: null,
last_name: null,
}
}
Change state of every input element onChange onChange={()=>this.setState({first_name: event.target.value})}
Now add onClick property to <Button onClick={this.handleSubmit} />
inside handleSubmit() you can get values through local state of component and add validation rules like
if(this.state.first_name =="") {
console.log("Enter first name")
}

Resources