React-Hook-Form - adding icon inside input when form validation goes wrong - reactjs

I am trying to create form with react and already took care of most of the validations (like displaying the message). I would also like to add icon/img inside the input when it is invalid. What would be the best approach to this?
Also, how to reset inputs after submitting form?
import React, { useEffect } from 'react';
import { useForm } from 'react-hook-form';
function Form() {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = (data) => {
console.log(data)
}
const registerOptions = {
email: {
required: "Email cannot be empty",
pattern: {
value: /^[\w-\.]+#([\w-]+\.)+[\w-]{2,4}$/,
message: "Looks like this is not an email"
}
}
}
return (
<div className="form">
<form onSubmit={handleSubmit(onSubmit)}>
<input
type="text"
placeholder='Email Address'
name="email"
{...register('email', (registerOptions.email)
)} />
{errors.email && <p>{errors.email.message}</p>}
<input type="submit" value="Claim your free trial" className="submit" />
</form>
</div>
)
}
export default Form;

To reset the Input fields you could do this:
const onSubmit = (data, e) => {
e.target.reset();
console.log(data);
}
If you want to add icons inside your inputs, I recommend you to search for bootstrap and https://www.npmjs.com/package/classnames

Related

Problem with using if statement in react-hook-form

Validation in react-hook-form using if, else statement return only when return value is true. But if the statement is false it returns nothing.
import * as React from "react";
import * as ReactDOM from "react-dom";
import { useForm } from "react-hook-form";
import "./styles.css";
const App = () => {
const {
register,
handleSubmit,
getValues,
formState: { isValid }
} = useForm({
mode: "onChange",
defaultValues: {
firstName: "",
lastName: ""
}
});
const onSubmit = () => {
if (isValid) {
console.log("valid");
} else {
console.log("invalid");
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>First Name</label>
<input type="text" {...register("firstName", { required: true })} />
<label>Last Name</label>
<input type="text" {...register("lastName", { required: true })} />
<input type="submit" />
</form>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
I don't know what's wrong. Can someone help please.
Here's the code from codesandbox
I don't think anything is wrong with the code. If you only enter the last name and hit submit, it highlights the first name box. And same for the opposite. If you only enter the first name and hit submit, it highlights the second box. This seems to me like a default behavior from the react hook form. Here you can see I added a useEffect to it to print out the validity. When the form is valid you can see it shows correctly.
This seems to be a feature from the react hook form to prevent submitting if it is not valid.
Edit: Look into their import { ErrorMessage } from "#hookform/error-message"; system they have in place. And Here is an updated code sandbox with error handling
Your form wont submit unless required fields are entered on inputs:
const onSubmit = () => {
if (!isValid) {
console.log("valid");
} else {
console.log("invalid");
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>First Name</label>
<input type="text" {...register("firstName")} />
<label>Last Name</label>
<input type="text" {...register("lastName")} />
<input type="submit" />
</form>
);
};

react-hook-form file field TypeScript type

I'm building a contact form with react-hook-form and Typescript. Following official examples here, I've implemented all the fields but the multiple file field.
What type should I use for a multiple-file field?
import * as React from "react";
import { useForm } from "react-hook-form";
type FormData = {
firstName: string;
lastName: string;
files: // <--- this is the type I'm looking for
};
export default function App() {
const { register, setValue, handleSubmit, formState: { errors } } = useForm<FormData>();
const onSubmit = handleSubmit(data => console.log(data));
// firstName and lastName will have correct type
return (
<form onSubmit={onSubmit}>
<label>First Name</label>
<input {...register("firstName")} />
<label>Last Name</label>
<input {...register("lastName")} />
<label>Files</label>
<input type="file" multiple {...register("files")} />
<button
type="button"
onClick={() => {
setValue("lastName", "luo"); // ✅
setValue("firstName", true); // ❌: true is not string
errors.bill; // ❌: property bill does not exist
}}
>
SetValue
</button>
</form>
);
}

DefaultValues of react-hook-form is not setting the values to the Input fields in React JS

I want to provide default values in the input field using react-hook-form. First I retrieve the user data from the API endpoint and then setting the state users to that user data. Then I pass the state users to the defaultValues of useForm().
import React, { useState, useEffect } from "react";
import { useForm } from "react-hook-form";
import axios from "axios";
function LoginFile() {
const [users, setUsers] = useState(null);
useEffect(() => {
axios
.get("http://localhost:4000/users/1")
.then((res) => setUsers(res.data));
}, []);
useEffect(() => {
console.log(users);
}, [users]);
const { register, handleSubmit, errors } = useForm({
defaultValues: users,
});
return (
<div>
<form onSubmit={handleSubmit(onSubmit)}>
Email <input type="email" name="email" ref={register} /><br />
firstname <input name="firstname" ref={register} /><br/>
<input type="submit" />
</form>
</div>
);
}
export default LoginFile;
I did by the above code but didn't work as expected. All the input fields are still empty. I want to have some default values in the input field of my form.
The problem is that during the first render, users is the useState hook's initial value, which is null. The value only changes after the axios.get() request finishes, which is after the initial render. This means that the the default values passed to useForm is null.
The documentation for defaultValues says the following:
defaultValues are cached on the first render within the custom hook. If you want to reset the defaultValues, you should use the reset api.
So, you'll just need to use reset to reset the form manually to the values which you fetch. The documentation for reset says the following:
You will need to pass defaultValues to useForm in order to reset the Controller components' value.
However, it's unclear from the documentation whether null is enough as the defaultValues, or if you need to pass it a proper object with fields for each input. To play it safe, let's assume it's the latter.
The code for doing this would look something like this:
function LoginFile() {
const [users, setUsers] = useState({ email: "", firstname: "" });
const { register, handleSubmit, errors, reset } = useForm({
defaultValues: users,
});
useEffect(() => {
axios.get("http://localhost:4000/users/1").then((res) => {
setUsers(res.data);
reset(res.data);
});
}, [reset]);
useEffect(() => {
console.log(users);
}, [users]);
return (
<div>
<form onSubmit={handleSubmit(onSubmit)}>
Email <input type="email" name="email" ref={register} />
<br />
firstname <input name="firstname" ref={register} />
<br />
<input type="submit" />
</form>
</div>
);
}
Additionally, if the only reason for the useState hook is to store the value for defaultValues, you don't need it at all and can clean up the code to be:
function LoginFile() {
const { register, handleSubmit, errors, reset } = useForm({
defaultValues: { email: "", firstname: "" },
});
useEffect(() => {
axios.get("http://localhost:4000/users/1").then((res) => {
reset(res.data);
});
}, [reset]);
return (
<div>
<form onSubmit={handleSubmit(onSubmit)}>
Email <input type="email" name="email" ref={register} />
<br />
firstname <input name="firstname" ref={register} />
<br />
<input type="submit" />
</form>
</div>
);
}
That definitely Worked. Using the reset API and the UseEffect.
Starting with empty stings as default values and the updating them as effects with the reset. Here is my code. Was using TypeScript with Ionic here as well...
const { control: editControl, handleSubmit: editSubmit, reset } = useForm<EditFormType>({
defaultValues: {
question: "",
optionA: "",
optionB: "",
optionC: "",
optionD: "",
}
});
useEffect(() => {
let defaults ={
question: editQuestion?.body,
optionA: editQuestion?.options[0].body,
optionB: editQuestion?.options[1].body,
optionC: editQuestion?.options[2].body,
optionD: editQuestion?.options[3].body,
}
reset(defaults)
}, [editQuestion, reset])

How to know all form data is validating in react-hook-form library

I am using react-hook-form for validation in reactjs. i am using multistep form, so once one step data is validated it should go to next step. But i didn't find how to know all data is validated or not. I am using if else condition for two buttons, once all data validated, then the button will go to next step.
here's my code..
import React, { useState } from 'react';
import { useForm } from "react-hook-form";
const UserDetails = ({ setForm, formData, navigation }) => {
const { handleSubmit, register, errors } = useForm();
const onSubmit = values => {
};
return(
<>
<input
type="text"
name="fullName"
id="fullname"
className="form__field form-control br0 font-white"
placeholder='Name'
onChange={setForm}
defaultValue={fullName}
ref={register({ required: true })}
/>
{errors.fullName && 'First name is required'}
//here my code for validating.
{allValid() ? (
<button className="btn green_btn w250 font22" onClick={next}>
Next
</button>
) : (
<button className="btn green_btn w250 font22" type="submit">Next</button>
)}
</>
)
}

redux-form v6: Form submission canceled because the form is not connected

I am getting this error in my console.
"Form submission canceled because the form is not connected"
after trying to migrate my redux-form from v5 to v6 since we migrated our app to a more recent version of React.
I am not sure what is going wrong here so i figured I could use a second or third pair of eyes.
Here is my "Smart Component"
import React, { PropTypes } from 'react';
import { reduxForm } from 'redux-form/immutable';
import { connect } from 'react-redux';
import { logUserIn } from '../../actions/authentication';
import { VALID_EMAIL_REGEX } from '../../config/app_config';
import LoginForm from './LoginForm';
const FORM_ID = 'loginForm';
export class LoginFormContainer extends React.Component {
static propTypes = {
handleSubmit: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired,
loginAction: PropTypes.func.isRequired,
};
performLogin = (params) => {
const { loginAction } = this.props;
const credentials = {
email: params.email,
password: params.password,
};
loginAction(credentials, '/home');
}
render() {
const { handleSubmit, submitting } = this.props;
return (
<LoginForm
handleSubmit={ handleSubmit }
loginFunction={ this.performLogin }
submitting={ submitting }
/>
);
}
}
const validate = values => {
const errors = {};
if (!values.email || values.email === '') {
errors.email = 'Required';
}
else if (!VALID_EMAIL_REGEX.test(values.email)) {
errors.email = 'Invalid email address';
}
if (!values.password || values.password === '') {
errors.password = 'Required';
}
return errors;
};
LoginFormContainer = reduxForm({
form: FORM_ID,
validate,
})(LoginFormContainer);
export default connect(null, {
loginAction: logUserIn,
})(LoginFormContainer);
I am passing down my submission handler function as a prop to my actual form that contains the Field component for inputs. The loginAction will link to the action for redux to send the values to the backend and redirect to home.
import React, { PropTypes } from 'react';
import { Field } from 'redux-form/immutable';
import { getClassName, checkButtonDisabled } from '../../utils/forms';
import { Link } from 'react-router';
const renderInput = (field) => {
return (
<div className={ getClassName(field.meta.touched, field.meta.error) }>
<input
{...field.input}
className="form-control form-control-success"
type={field.type}
/>
{field.meta.touched &&
field.meta.error &&
<span className="error">{field.meta.error}</span>}
</div>
);
};
export default class LoginForm extends React.Component {
static propTypes = {
handleSubmit: PropTypes.func.isRequired,
loginFunction: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired,
};
render() {
const {
loginFunction,
submitting } = this.props;
return (
<form onSubmit={ loginFunction.bind(this) }>
<fieldset>
<div>
<label className="form-control-label">
Email address
</label>
<Field
name="email"
component={renderInput}
type="text"
placeholder="example#exampledomain.com"
/>
</div>
<div>
<label className="form-control-label">
Password
</label>
<Field
name="password"
component={renderInput}
type="password"
placeholder="your password"
/>
</div>
</fieldset>
<button
type="submit"
className="btn btn-primary"
disabled={ checkButtonDisabled(submitting) }
>
Log In
</button>
<Link to="/forgot-password">Forgot Password?</Link>
</form>
);
}
}
I successfully was able to get the form to work but when I hit login I get the error above and I am redirected to home, but I am not authenticated and I get a 422 error as well. I couldn't tell if my form connecting is the only error or if my action is not getting the right information from the form submission function.
Got any suggestions?
You are redirected home, because your loginFunction() is fired, but the form is not submitted
There are a couple of things that need to be updated. Your <form> tag must have a corresponding id and it should handle submit by passing your function to redux-form inbuilt submit handler. So you modifying LoginForm class as follows should get the form working
<form id="loginForm" onSubmit={ handleSubmit(this.loginFunction.bind(this)) } >
More about internal redux-form method handleSubmit here: http://redux-form.com/6.5.0/docs/api/Form.md/
Using the answer given to me above I just wanted to clarify what I did to solve the issue.
I grabbed the handleSubmit method that comes from the reduxForm and passed it to the LoginForm as a prop from the container that also retrieved it from the props.
I also imported the Form component from redux-form on the LoginForm component and just simply replaced the normal JSX tag with .
here were the final changes I made.
LoginForm.jsx:
//Added Form from redux-form
import { Field, Form } from 'redux-form/immutable';
render() {
const {
handleSubmit,//defined handleSubmit function from props which comes from the reduxForm being exported to the state.
loginFunction,
submitting } = this.props;
return (
//replaced <form></form> with <Form></Form>
<Form id="loginForm" onSubmit={ handleSubmit(loginFunction.bind(this)) }>
//passed my loginFunction into the handleSubmit function
//added an id to <Form> that corresponds to the forms id that was passed in reduxForm({ form: 'loginForm' })
<fieldset>
<div>
<label className="form-control-label">
Email address
</label>
<Field
name="email"
component={renderInput}
type="text"
placeholder="example#exampledomain.com"
/>
</div>
<div>
<label className="form-control-label">
Password
</label>
<Field
name="password"
component={renderInput}
type="password"
placeholder="your password"
/>
</div>
</fieldset>
<button
type="submit"
className="btn btn-primary"
disabled={ checkButtonDisabled(submitting) }
>
Log In
</button>
<Link to="/forgot-password">Forgot Password?</Link>
</Form>
);

Resources