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

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

Related

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

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>
);
}

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();

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

How to get the url to insert as a data with Redux Form?

I was able to get the url in the attachment field but for the redux form it was empty, how is possible to pass the value of the url to the redux form? Below is the code and the screenshot:
<div className="FileUpload">
<Dropzone
onDrop={this.onImageDrop.bind(this)}
multiple={false}
accept="image/*">
<div>Drop an image or click to select a file to upload.</div>
</Dropzone>
</div>
<div className="form-group">
<label htmlFor="attachment">Attachment:</label><br />
<input className="form-control" focus placeholder="attachment" type="text" name="attachment" ref="attachment" value={this.state.uploadedFileCloudinaryUrl} />
{this.state.uploadedFileCloudinaryUrl === '' ? null :
<div>
<p>{this.state.uploadedFile.name}</p>
<img src={this.state.uploadedFileCloudinaryUrl} alt="" />
</div>}
</div>
<div className="ui small image">
<img src={this.props.workrequest.attachment} alt="" />
</div>
the url in the attachemnt field
The first one is using the React Dropzone to get the url but for the Redux Form it was empty. May I know how to do that to get the url inserts at Redux Form here? Thank you
import React from 'React';
import { connect } from 'react-redux';
import { change, reduxForm } from 'redux-form';
import Dropzone from 'react-dropzone';
class UploadForm extends React.Component {
onDrop = (accepted) => {
if (!accepted.length) return;
// start uploading
this.setState({ isUploading: true });
const formData = new FormData();
formData.append('file', accepted[0]);
axios.post('upload', formData).then(
(res) => {
this.props.dispatch(change('uploadForm', 'url', res.url));
this.setState({ isUploading: false });
},
() => {
this.setState({ isUploading: false });
}
);
};
render() {
return (
<form>
<Dropzone onDrop={this.onDrop} />
</form>
);
}
}
export default connect()(
reduxForm({
form: 'uploadForm',
initialValues: {
url: ''
}
})(UploadForm)
);
Please use this.
Actually, you must use Field component from Redux-form.
Other wise, you can change form values using dispatch and change action creator.
import { Field, change } from 'redux-form';
this.props.dispatch(change('uploadForm', 'url', this.state.url));

Resources