react-hook-form: Failed to set the 'value' property on 'HTMLInputElement' - react-hook-form

I am trying to use react hook form for user input to upload file
import "./styles.css";
import { useForm, Controller } from "react-hook-form";
import {
Col,
Row,
Form,
FormGroup,
InputGroup,
Input,
Container,
Button
} from "reactstrap";
export default function App() {
const onSubmit = (data) => {
console.log(data);
};
const { control, handleSubmit } = useForm();
return (
<Container>
<Form onSubmit={handleSubmit(onSubmit)}>
<Row className="m-3">
<Col>
<FormGroup row className="mr-md-1">
<InputGroup className="mb-3">
<Controller
name="itemlist2"
control={control}
render={({ field: { ref, ...field } }) => (
<Input
{...field}
type="file"
required
innerRef={ref}
onChange={(e) => {
field.onChange(e.target.files);
}}
/>
)}
/>
</InputGroup>
</FormGroup>
</Col>
</Row>
<Button color="primary" className="mr-1">
{"Save Changes"}
</Button>
</Form>
</Container>
);
}
Check on https://codesandbox.io/s/affectionate-moon-dmn8q
I get

I'm not very familiar with reactstrap, but i think you have to omit the value prop which is part of field. You can’t set the
value of an input with type="file". Check this answer for more infos.
<Controller
name="itemlist2"
control={control}
render={({ field: { value, ...field } }) => (
<Input
{...field}
type="file"
required
innerRef={field.ref}
onChange={(e) => {
field.onChange(e.target.files);
}}
/>
)}
/>

The issue is due to the fact that you are using a controlled input and not passing value to render it out on onSubmit.
Use { field: { value, ...field } } as props to the render method and it will set the 'value' property on 'HTMLInputElement' otherwise it will only pass an empty string which is in conflict with a file type data.
It should be like this <input type="file" value="c:/js.txt"/> but you are doing like this <input type="file"/>
Complete Code:
import "./styles.css";
import { useForm, Controller } from "react-hook-form";
import {
Col,
Row,
Form,
FormGroup,
InputGroup,
Input,
Container,
Button
} from "reactstrap";
export default function App() {
const onSubmit = (data) => {
console.log(data);
};
const { control, handleSubmit } = useForm();
return (
<Container>
<Form onSubmit={handleSubmit(onSubmit)}>
<Row className="m-3">
<Col>
<FormGroup row className="mr-md-1">
<InputGroup className="mb-3">
<Controller
name="itemlist2"
control={control}
render={({ field: { value, ...field } }) => (
<Input
{...field}
type="file"
required
innerRef={field.ref}
onChange={(e) => {
field.onChange(e.target.files);
}}
/>
)}
/>
</InputGroup>
</FormGroup>
</Col>
</Row>
<Button color="primary" className="mr-1">
{"Save Changes"}
</Button>
</Form>
</Container>
);
}

Related

react-hook-form: how to remove the item (which is mentioned in defautvalue) from submit data if its not mounted. Tried unregister its not working

I have simple form with firstName and lastName. I have added some defaultValues to them in useForm. I havent mounted the lastName using if condition.
Now when I try to submit, I am expecting it to show only the mounted components values i.e firstName. But it shows both firstName and lastName. I have created a button to try to unregister("lastName") but even after unregister and not being mounted it shows in the submit data.
If I dont provide defaultValues then it works well, like if not mounted initially it will not be shown in the submit data.
Below is the code
import React, { useState } from "react";
import ReactDOM from "react-dom";
import { useForm } from "react-hook-form";
import "./styles.css";
const App = () => {
const { register, handleSubmit, unregister, watch } = useForm({
defaultValues: {
firstName: "test",
lastName: "test2"
}
});
const onSubmit = (data) => {
console.log("ON SUBMIT");
console.log(JSON.stringify(data, null, 4));
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>Last Name</label>
<input {...register("firstName")} />
{false && (
<>
<label>Last Name</label>
<input {...register("lastName")} />
</>
)}
<button
type="button"
onClick={() => {
console.log("unregistering lastName")
unregister("lastName");
}}
>
unregister lastName
</button>
<input type="submit" />
</form>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
And the codesandbox
For the first, you can't use unregister("lastName"); before register("lastName"); it's useless. Second, when you use defaultValues, method unregister does not remove them but simply set values to default. You just can filter submit data by conditions without unregister. Demo

TypeError: Cannot read property 'message' of undefined ---using react-hook-form

I am trying to display an error message when nothing is typed inside the message input form, but when I load the page I get this error 'TypeError: Cannot read property 'message' of undefined'. I am using react-hook-forms. This is my code down below.
import { Button } from "#material-ui/core";
import { Close } from "#material-ui/icons";
import React from "react";
import { useForm } from "react-hook-form";
import "./SendMail.css";
const SendMail = () => {
const { register, handleSubmit, watch, errors } = useForm();
const onSubmit = (formData) =>{
console.log(formData)
}
return (
<div className="sendMail">
<div className="sendMail__header">
<h3>New Message</h3>
<Close className="sendMail__close" />
</div>
<form onSubmit={handleSubmit(onSubmit)}>
<input name='to' placeholder="To" type="text" {...register('to',{required:true})}/>
<input name="subject" placeholder="Subject" type="text" {...register('subject',{required:true})} />
<input
name="message"
placeholder="Message..."
type="text"
className="sendMail__message"
{...register('message',{required:true})}
/>
{errors.message && <p>To is required!!</p>}
<div className="sendMail__send">
<Button
className="sendMail__send"
variant="contained"
color="primary"
type="submit"
>
Send
</Button>
</div>
</form>
</div>
);
};
export default SendMail;
Since v7 the errors object moved to the formState property, so you need to adjust your destructering:
const { register, handleSubmit, watch, formState: { errors } } = useForm();

How to register 'react-bootstrap-typeahead' component using React 'useForm' hook?

I'm building a simple html form using "react-hook-form" library: https://react-hook-form.com/
I've incorporated "react-bootstrap-typeahead" into the html form but haven't been able to register this component with 'useForm' hook. Hence, "react-bootstrap-typeahead" input data is ignored during onSubmit.
"react-bootstrap-typeahead" doesn't provide a "name" prop which makes it difficult to register the component.
I've read the 'useForm' documentation on the different options for registering this type of components but still don't understand how to achieve this: https://react-hook-form.com/get-started#Registerfields
Does anybody have faced such challenge before?
It would be great to see a working example to get a better idea on how to implement "react-bootstrap-typeahead" + "react-hook-form" in my application. Thanks!
Here's my sample code:
import useForm from 'react-hook-form';
import { Typeahead } from 'react-bootstrap-typeahead';
import 'react-bootstrap-typeahead/css/Typeahead.css';
const myForm = (props) => {
const { register, handleSubmit, errors } = useForm();
const onSubmit = data => {
// api post request with form data
})
};
const mydata = [ "one", "two", "three" ];
return (
<>
<form onSubmit={handleSubmit(onSubmit)} >
<div className="form-group">
{/* Here I'm registering text input using ref: */}
<input type="text" className="form-control" name="name" ref={register({ required: true })} />
</div>
<div className="form-group mb-0">
{/* How can I register the below component with useForm? */}
<Typeahead
id="multiple-typeahead"
clearButton
multiple
options={mydata}
/>
</div>
<button type="submit">Save</button>
</form>
</>
);
}
This is how i was able to register the component:
import useForm from 'react-hook-form';
import { useForm, Controller } from "react-hook-form";
import 'react-bootstrap-typeahead/css/Typeahead.css';
const myForm = (props) => {
const { register, handleSubmit, errors, control } = useForm();
const onSubmit = data => {
// api post request with form data
})
};
const mydata = [ "one", "two", "three" ];
return (
<>
<form onSubmit={handleSubmit(onSubmit)} >
<div className="form-group">
<input type="text" className="form-control" name="name" ref={register({ required: true })} />
</div>
<div className="form-group mb-0">
<Controller
as={Typeahead}
control={control}
name="typeahead_component"
rules={{ required: true }}
id="multiple-typeahead"
clearButton
multiple
options={mydata}
defaultValue=""
/>
</div>
<button type="submit">Save</button>
</form>
</>
);
}

Dynamic name for react-final-form Field

I have 2 forms. When I choose an option on 1st form, the 2nd form is added to the page with the parameters retrieved from backend. Now how can I set the parameter names as react-final-form Field names?
I could not find a way to do this. Where to pass the parameter names?
<Form
onSubmit={onSubmit}
validate={validate}
React Final Form calls your onSubmit function with the values from all the fields in your form. It's totally up to you to transmit the values to your server.
If you're asking how to build the second form, you just add the fields you need to add. So, say you got back from the server that you needed three fields: [ 'name', 'startTime', 'endTime' ]. You'd just loop through that array and add the fields.
<Form onSubmit={onSubmit}>({handleSubmit}) => (
<form onSubmit={handleSubmit}>
{fieldsFromServer.map(fieldName => (
<div key={fieldName}>
<label>{fieldName}</label>
<Field name={fieldName} component="input"/>
</div>
))}
</form>
)}<Form>
Does that help? You don't have to "pass parameters to the form", you just add the Field components that you need.
Call the FinalForm like
<FinalFieldArrayForm onSubmit={this.handleSubmitTemplate} fieldsFromServer={parameters} />
and FinalForm is
import React from "react";
import ReactDOM from "react-dom";
import { Form, Field } from 'react-final-form'
import arrayMutators from 'final-form-arrays'
import { FieldArray } from 'react-final-form-arrays'
import "./styles.css";
const FinalForm = ({onSubmit, fieldsFromServer}) => (
<Form
onSubmit={onSubmit}
mutators={{
// potentially other mutators could be merged here
...arrayMutators
}}
render={({
handleSubmit,
form: {
mutators: { push, pop }
},
pristine,
form,
submitting,
values
}) => (
<form onSubmit={handleSubmit}>
<div className="buttons">
<button type="button" onClick={() => push('records', undefined)}>+</button>
<button type="button" onClick={() => pop('records')}>-</button>
<button type="button" onClick={form.reset} disabled={submitting || pristine}>Reset</button>
</div>
<FieldArray name="records">
{ ({fields}) => (
<div>
{fields.map( (name, index) => (
<div key={`${name}.${index}`}>
<label>{index + 1}</label>
{fieldsFromServer.map( param => <Field key={`${name}.${param}`} name={`${name}.${param}`} component="input" placeholder={`${name}.${param}`} /> )}
<button type="button" onClick={() => fields.remove(index)}>-</button>
<button type="button" onClick={() => fields.insert(index+1)}>+</button>
</div>
))}
</div>
)}
</FieldArray>
<div className="buttons">
<button type="submit" disabled={submitting || pristine}>Submit</button>
</div>
<pre>{JSON.stringify(values, 0, 2)}</pre>
</form>
)}
/>
)
const rootElement = document.getElementById("root");
ReactDOM.render(<FinalForm onSubmit={() => (<div/>)} fieldsFromServer={["firstName", "lastName"]} />, rootElement);

Automatic "for" and "id"

Because we cannot assume our components are singletons: I am trying to automatically handle giving htmlFor to a label and field. We see below I use useMemo and lodash uniqueId to memoize a unique id for the form on initial render. I had to give useMemo and empty array as second argument so it never re-calculates the id. Is there some automated way to handle this in final-form?
import React, { useMemo } from 'react';
import { Form, Field } from 'react-final-form';
import { uniqueId } from 'lodash';
function TaskForm() {
const id = useMemo(() => uniqueId('_form'), []);
const getFor = name => name + id;
return (
<>
<h3>Create a task</h3>
<Form onSubmit={onSubmit}>
{({ handleSubmit, pristine, invalid, ...rest }) => {(
<form onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor={getFor('firstName')}>First Name</label>
<Field name="firstName" id={getFor('firstName')} component="input" placeholder="First Name" />
</div>
<button type="submit" disabled={pristine || invalid}>Submit</button>
</form>
)}}
</Form>
</>
);
}

Resources