I'm trying to create separate input component and use React Hook Forms, Here is component:
export default function TextInput({label, id, placeholder, name, ...params}) {
return (
<FormGroup>
<Label htmlFor={id}> {label} </Label>
<Input
className="form-control"
placeholder={placeholder}
name={name}
id={id}
{...params}
/>
</FormGroup>
);
}
and I'm calling it like this:
const {register, handleSubmit} = useForm();
const onSubmit = (data) => {
console.log(data);
}
<form onSubmit={handleSubmit(onSubmit)}>
<TextInput
label="input password"
placeholder="password"
name="password"
{...register("password")}
id="password"
type="password"
/>
<button type="submit">submit</button>
</form>
But when I click submit, password seems to be undefined
React-hook-version is 7.33, any ideas about what is the problem?
lets say you passed the a varible to get value from Input Field.
and next step will be to give this input field a function so that it can update the value of that variable in your case passowrd.
to do that I will use onChange.
<input onChange={(event)=>{resister.password = event.target.value}}/>
or
<input onChange={(event)=>{resister['password'] = event.target.value}}/>
and according to useForm documentation
you should be using resister in <input/> not in <TextInput/>
const { onChange, onBlur, name, ref } = register('firstName');
// include type check against field path with the name you have supplied.
<input
onChange={onChange} // assign onChange event
onBlur={onBlur} // assign onBlur event
name={name} // assign name prop
ref={ref} // assign ref prop
/>
// same as above
<input {...register('firstName')} />
https://react-hook-form.com/api/useform/register
Related
I am totally new to react, I have sign up form which have 5 five input fields.
I created one functional component for TextField
MyTextField.js
function MyTextField(props) {
return (
<TextField
label={props.label}
name={props.name}
placeholder={props.placeholder}
type={props.type}
onChange={props.handleOnChange}
value={props.value}
></TextField>
)
}
export default MyTextField
Signup.js
function SignUp() {
const [value, setValue] = useState('')
function FunConfirmPassword(event) {
setValue = event.target.value //this function is calling, if I comment this line
console.log('FunConfirmPassword called - password value = '+ value)
}
return (
<MyTextField type="text" label="Firstname" name="firstName" placeholder="Enter Firstname"></MyTextField>
<MyTextField type="text" label="Lastname" name="Lastname" placeholder="Enter Lastname"></MyTextField>
<MyTextField type="text" label="emailID" name="emailID" placeholder="Enter emailID"></MyTextField>
<MyTextField type="password" label="password" name="password" placeholder="Enter password"></MyTextField>
<MyTextField handleOnChange={FunConfirmPassword} type="password" label="confirmpassword" name="confirmpassword" placeholder="Enter confirmpassword"></MyTextField>
)
}
I am trying to learn how to set the value of TextField and get the value of same textfield.
At the same time, it must be re-usable function
how to set the value of TextField
In your code, the <TextField> gets its value from a prop:
value={props.value}
So to supply that value, pass it as that prop:
<MyTextField value={value} ...
and get the value of same textfield
That you're already doing here:
<MyTextField handleOnChange={FunConfirmPassword} ...
However, you have a mistake in your handler function here:
setValue = event.target.value
setValue is a function. This should be:
setValue(event.target.value);
Additionally, the console.log statement immediately following it won't print what you expect. React state updates are asynchronous and batched. Either log event.target.value instead, or use a useEffect to respond to the updated value value and log it there. (Or don't log it to the console at all, it's not really necessary.)
I've prepared a codepen example for you, where you can see how this works.
https://codepen.io/dns_nx/pen/BaxjdKd
Basically, you set a property to the child component, in which you define the function which is being executed, when called by the child (see property onChange).
To set the value in the MyTextField component, you just have to pass the current value of the variable in SignUp to your MyTextField component (see property value):
function SignUp(props) {
const [firstName, setFirstName] = useState('');
...
<MyTextField
type="text"
label="Firstname"
name="firstName"
placeholder="Enter Firstname"
value={firstName}
onChange={(event) => setFirstName(event.target.value}
/>
...
}
Then you need to call the function where you need it in your child component (i.e. onChange event) to pass the new value to the parent component (SignUp). The value is set by the property value:
function MyTextField(props) {
...
<TextField
...
value={props.value}
onChange={(event) => props.onChange(event)}
...
></TextField>
}
This is the way to pass values from child components to parent components.
But as larger your application gets, it makes it hard to handle. Therefore I would recommend you to use a state management library like Redux (react-redux).
https://www.npmjs.com/package/react-redux
and use this with the toolkit package
https://www.npmjs.com/package/#reduxjs/toolkit
Previously I used to write like this:
<input className="form-control" name="productImage" type='file' onChange={handleImageUpload} ref={register({ required: true })} />
After the update I have to write like this:
<input className="form-control" type="file" {...register('productImage', { required: true })} />
How do I use onChange={handleImageUpload} on the updated version of React Hook Form?
Here is the migration docs
Please pardon my mistakes in the manner of asking the question. I'm new to these things.
Thank you.
https://github.com/react-hook-form/react-hook-form/releases/tag/v7.16.0
V7.16.0 has introduced this new API for custom onChange.
<input
type="text"
{...register('test', {
onChange: (e) => {},
onBlur: (e) => {},
})}
/>
You just have to move the onChange props after {...register(...)}
const productImageField = register("productImage", { required: true });
return (
<input
className="form-control"
type="file"
{...productImageField}
onChange={(e) => {
productImageField.onChange(e);
handleImageUpload(e);
}}
/>
)
(Dec 3 2021) edit: this approach is no longer correct since react-hook-form v7.16.0's changes, see #Bill's answer.
In register documentation https://react-hook-form.com/api/useform/register, sample exists on Custom onChange, onBlur section :
// onChange got overwrite by register method
<input onChange={handleChange} {...register('test')} />
// register's onChange got overwrite by register method
<input {...register('test')} onChange={handleChange}/>
const firstName = register('firstName', { required: true })
<input
onChange={(e) => {
firstName.onChange(e); // method from hook form register
handleChange(e); // your method
}}
onBlur={firstName.onBlur}
ref={firstName.ref}
/>
So for your case :
const productImageRegister = register("productImage", {required: true})
<input className="form-control"
type="file"
{...productImageRegister }
onChange={e => {
productImageRegister.onChange(e);
handleImageUpload(e);
}} />
For me, decoration solution worked
const fieldRegister = register("productImage", {required: true})
const origOnChange = fieldRegister.onChange
fieldRegister.onChange = (e) => {
const res = origOnChange(e)
const value = e.target.value
// do something with value
return res
}
For field declaration use
<input {...fieldRegister}/>
Was stuck with the same problem. For me the problem was that my onChange was above the react-hook-form's {...register} and moving it below the register solved the problem for me!!
I faced a similar issue recently when migrating to V7. If it can help anybody.
A parent component handling the form was passing down to a wrapper the register function, the wrapper passing it down again to an input that needed debouncing on change.
I called the register formLibraryRef in case I wanted to use a different library later but overall I had to do something like:
const { onChange, ...rest } = formLibraryRef(inputName);
pass the onChange to the function that is itself passed to the native onChange event of the input:
const handleDebouncedChange: (event: React.ChangeEvent<HTMLInputElement>) => void = (
event: ChangeEvent<HTMLInputElement>,
) => {
onChange(event);
if (preCallback) {
preCallback();
}
debounceInput(event);
};
and then pass the rest to the input:
<input
aria-label={inputName}
name={inputName}
data-testid={dataTestId}
className={`form-control ...${classNames}`}
id={inputId}
onChange={handleDebouncedChange}
onFocus={onFocus}
placeholder={I18n.t(placeholder)}
{...rest}
/>
The register section in the docs here: https://react-hook-form.com/migrate-v6-to-v7/ gives a bit more info on how to get the onChange and shows an example for Missing ref.
You can use react-hook-form control
<Controller
render={({ field }) => <input onChange={event=>{
handleImageUpload(event);
field.onChange(event);
}} />}
name="image"
control={control}
/>
render form to input values:
return (
handleSubmit will check if the form is valid and ready to be submit, it call the callback function
// that is in our case, the onSubmit()
The name property specifies what piece of state is being edited
<Field
label="Email"
type="email"
name="Email"
component={this.renderField}
/>
<Field name="city"
label="Cidade"
value={this.state.value}
onChange={this.handleChange}
options={options}
component="select">
</Field>
<Field
label="Password"
type="password"
name="password"
component={this.renderField}
/>
<button type="submit" className="btn btn-primary">Submit</button>
<Link to="/register" className="btn btn-danger">Already registered?</Link>
</form>
);
I'm getting the values from json but the field it's empty
Your initial state might not be set, so this.state.cities is actually empty.
Do something like this in your render function:
render() {
const options = ( this.state.cities || [] ).map( (city) => ( {
value: city.name,
label: city.id
} );
return (
-- other components --
{ options.length ? (
<Field name="city"
label="Cidade"
value={this.state.value}
onChange={this.handleChange}
options={options}
component="select"
/>
) : null }
-- other components --
);
}
( this.state.cities || [] ) will check if this.state.cities is available, otherwise it uses an empty array.
A little more detail: Your axios call is asynchronous. That means, that React doesn't wait for axios to fetch your data but instead just tries to render something.
Your state has not been set (probably) and therefore you get this error.
Here we have to write three functions to change the state of all input fields of form and in case if we have more fields e.g phone and address we have to write two more functions to change the state of those fields, i want to ask is there any way we can write only a single generic function to change the state of any field of this form rather than write separate function for every form field?
class SignUpForm extends React.Component {
constructor() {
super();
this.state = {
name: '',
email: '',
password: '',
};
}
handleNameChange = (evt) => {
this.setState({name: evt.target.value});
}
handleEmailChange = (evt) => {
this.setState({email: evt.target.value});
}
handlePasswordChange = (evt) => {
this.setState({password: evt.target.value});
}
render(){
return(
<form onSubmit={this.handleSubmit}>
<input
type="text"
placeholder="Enter Name"
value={this.state.name}
onChange={this.handleNameChange}
/>
<input
type="text"
placeholder="Enter email"
value={this.state.email}
onChange={this.handleEmailChange}
/>
<input
type="password"
placeholder="Enter password"
value={this.state.password}
onChange={this.handlePasswordChange}
/>
<button disabled={isDisabled}>Sign up</button>
</form>
)
}
}
You could use the following pattern:
handleChange = (type, event) => {
this.setState({[type]: event.target.value});
}
You use handleChange like that:
<input
type="text"
placeholder="Enter Name"
value={this.state.name}
onChange={(event) => this.handleChange('name', event)}
/>
There's an even cleaner way to write this using currying:
handleChange = type => event => this.setState({[type]: event.target.value})
<input
type="text"
placeholder="Enter Name"
value={this.state.name}
id="name"
onChange={this.handleChange('name')}
/>
You could even use the element's ID attribute to avoid the instantiation of a new handler every time you render (since this.handleChange('name') would return a new instance of event => this.setState({name: event.target.value}) every render), and to avoid repeating yourself. However this may not be advisable since it means your element IDs must match your state keys.
handleChange = event => this.setState({[event.target.id]: event.target.value})
<input
type="text"
placeholder="Enter Name"
value={this.state.name}
id="name"
onChange={this.handleChange}
/>
I am not able to access the value of <TextField />, if i don't write <input type='password'/> then it works fine, but for this i am getting a TypeError, 'this.refs[this._getRef(...)].getInputNode is not a function'.
dialogAction(tag,e){
console.log(this.refs.password);
console.log(this.refs.password.getValue());
this.refs.dialog.dismiss();
}
render(){
let self = this;
let row = this.row,col = this.column;
let standardActions = [
{ text: 'Cancel',onTouchTap: this.dialogAction.bind(this,ProductConstants.CANCEL)},
{ text: 'Submit',onTouchTap: this.dialogAction.bind(this,ProductConstants.SUBMIT)}
];
return (
<div className="ProductRepository">
<Dialog ref = 'dialog'
title="Dialog With Standard Actions"
actions={standardActions}
actionFocus="submit"
modal={true}>
<TextField ref='password'
hintText="Password"
floatingLabelText="Password">
<input type="password" />
</TextField>
</Dialog>
</div>
);}
}
image below is the console output of the above code.
This solved my issue:
<TextField ref='password'
hintText="Password"
floatingLabelText="Password"
type="password">
</TextField>
After that
this.refs.password.getValue()
gives the desired output.
For React v >= 15.6
<TextField ref={x => this.password = x}
hintText="Password"
floatingLabelText="Password"
type="password">
</TextField>
in inputHandler function
this.password.value
For material 1.0 and react 16.1.1
Use inputRef
<TextField autoFocus={true} inputRef={el => this.fv = el}
placeholder="Required" size="30"></TextField >
To read the value use below line
console.log(this.fv.value);
Assign ref="password" to the input itself instead of the TextField. Currently you are executing getValue() on some abstract (probably some container) tag (TextField), not on the input itself.
Here's how it's done.
You can get the input value like this :
this.refs.password.input.value;