Check if React Bootstrap Form is filled in or not to conditionally disable submit button - reactjs

We have the following contact form in React using https://react-bootstrap.github.io/forms/overview/
let contactForm =
(<Form ref={formRef} onSubmit={sendEmail} className='toggle-contact-form'>
<div className='toggle-contact-form__header'>
<p className='p1'>Please Reach out!</p>
<p className='p2'>Use our contact form to reach out with any questions, concerns or issues with the website.</p>
</div>
<Form.Row style={{ paddingTop: 20 }}>
<Form.Group as={Col} controlId='name'>
<Form.Label>Name</Form.Label>
<Form.Control className='cbb-home-input' placeholder='required' />
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col} controlId='email'>
<Form.Label>Email Address</Form.Label>
<Form.Control className='cbb-home-input' type='email' placeholder='required' />
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col} controlId='phone'>
<Form.Label>Phone Number</Form.Label>
<Form.Control className='cbb-home-input' placeholder='optional' />
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col} controlId='message'>
<Form.Label>Message</Form.Label>
<Form.Control className='cbb-home-input' as='textarea' rows='2' placeholder='required' />
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col} controlId='button'>
<Button variant='primary' type='submit' disabled={true}>
{isSubmitting ? 'Sending Email...' : 'Submit'}
</Button>
</Form.Group>
</Form.Row>
</Form>);
Currently the button disabled={true}, we'd like to make this conditional on the Form.Control elements for name, message both being not empty, and email being a valid email address. Currently we have no form validation. Is it possible to validate this form as such?

The Bootstrap docs suggest using a library to make this process easier:
It's often beneficial (especially in React) to handle form validation via a library like Formik, or react-formal. In those cases, isValid and isInvalid props can be added to form controls to manually apply validation styles.
But here's how you can do it without a library:
Since we need access to the values of the input fields, we'll need to use controlled components to hold the form data. First we will set up some useState variables to hold the data:
const [name, setName] = useState("");
const [message, setMessage] = useState("");
const [email, setEmail] = useState("");
Then we need to use those state variables to handle the data in form fields by setting the value and onChange props:
...
<Form.Control
value={name}
onChange={(e) => {
setName(e.target.value);
}}
className="cbb-home-input"
placeholder="required"
/>
...
<Form.Control
value={email}
onChange={(e) => {
setEmail(e.target.value);
}}
className="cbb-home-input"
type="email"
placeholder="required"
/>
...
<Form.Control
value={message}
onChange={(e) => {
setMessage(e.target.value);
}}
className="cbb-home-input"
as="textarea"
rows="2"
placeholder="required"
/>
...
Now that we have access to the form field data, we can create a variable to keep track of whether the user input is valid:
const isValid = checkValidity(name, message, email);
The checkValidity function can check if name, message, and email meet the requirements we want them too:
const checkEmail = (email) => {
return /^\w+#[a-zA-Z_]+?\.[a-zA-Z]{2,3}$/.test(email);
};
const checkValidity = (name, message, email) => {
return !!name && !!message && checkEmail(email);
};
At this point, the isValid variable will always be updated with whether or not the current user input in the form is valid. Specifically, we are making sure name and message are not empty, and that email passes a simple regex validity check.
Finally, we disable the submit button whenever isValid is false using the disabled prop:
<Button variant="primary" type="submit" disabled={!isValid}>
{isSubmitting ? "Sending Email..." : "Submit"}
</Button>
Here's a full working example on CodeSandbox:

Related

Inputs not updating state in functional react component

I looked around and I see similar questions, but whenever I follow the answers I can't seem to get this to work in the way that I have it written. I am starting off all four states as blank inside of an array, but I want to update the states as the user types for each input field. Why is setChanging not working to update the state for the particular name of the input field? Console logs both the x and y values as I type into each input. I must be doing something simple wrong, but I can't figure out what it is.
const ContactForm = () => {
const initialValues = {
recipientName: "",
recipientEmail: "",
fromName: "",
fromEmail: "",
};
const [changing, setChanging] = useState(initialValues);
const handleInputChange = (e) => {
let x = e.target.name;
let y = e.target.value;
console.log(x);
console.log(y);
setChanging({...changing, [e.target.name]: e.target.value});
console.log(initialValues);
}
return (
<Form>
<Form.Group className="mb-3">
<Row>
<Col>
<Form.Control
type="text"
required
name="recipientName"
placeholder="Recipient Name*"
id="form.recipientName"
onChange={handleInputChange}
/>
</Col>
<Col>
<Form.Control
type="email"
required
name="recipientEmail"
placeholder="Recipient Email*"
id="form.recipientEmail"
onChange={handleInputChange}
/>
</Col>
</Row>
</Form.Group>
<Form.Group className="mb-3">
<Row>
<Col>
<Form.Control
type="text"
required
name="fromName"
placeholder="From Name*"
aria-invalid="form.fromName"
onChange={handleInputChange}
/>
</Col>
<Col>
<Form.Control
type="email"
required
name="fromEmail"
placeholder="From Email*"
id="form.fromEmail"
onChange={handleInputChange}
/>
</Col>
</Row>
</Form.Group>
<Button variant="primary" type="submit">
Submit
</Button>
</Form>
);
}
export default ContactForm
You're logging the initialValues:
console.log(initialValues);
So you're always seeing the value of initialValues, which never changes. Nowhere are you observing state.
You can respond to state updates and log the updated state with useEffect for example:
useEffect(() => console.log(changing), [changing]);
This would log the value of changing any time that value changes.
You'd also observe updates to state in the UI if/when your UI uses state to render output. (Currently it does not.)
There are some things I suggest you to change:
<Form.Control
type="text"
required
name="fromName"
placeholder="From Name*"
aria-invalid="form.fromName"
onChange={handleInputChange}
/>
I'm not sure if those components belong to a framework like MaterialUI but would be better to have an attribute called value where you pass the state to handle a controlled component instead of an uncontrolled component:
<Form.Control
type="text"
required
name="fromName"
placeholder="From Name*"
aria-invalid="form.fromName"
onChange={handleInputChange}
value={changing.fromName} // Add this attribute
/>
Also, would be better if your initialState is outside of the function.
console.log(initialValues);
You should print the state instead of the initialValues, what you are updating is the state, not the initialValues.
setChanging({...changing, [e.target.name]: e.target.value});
this is because you didn't specify value attribute on your inputs.
in addition to this to see the changes on user type you must console.log the state (which is change here) not the initialValues.
example:
<Form.Control
type="text"
required
name="recipientName"
placeholder="Recipient Name*"
id="form.recipientName"
value={changing.fromName}
onChange={handleInputChange}
/>

Form validation using React, EmailJS and Bootstrap

So I'm trying to get my form validation to work using EmailJs and Bootstrap within React for a simple contact form. I got the validation to work on the UI, however, it will still send the email even if the fields are red and required. Any idea what I'm doing wrong? I followed the examples here: https://react-bootstrap.github.io/forms/validation/ and here: https://www.emailjs.com/docs/examples/reactjs/ but it does not seem to be working correctly.
Thanks in advance for any help you can provide.
EDIT
I was able to solve this by using a handleSubmit function after my sendEmail function. Please see updated code below.
I also added a way to clear the form by setting setValidated(false); e.target.reset(); within .then() method after the user submits the form...Hopefully this will help others struggling with this.
import React, { useRef, useState } from "react";
import emailjs from "#emailjs/browser";
import Button from "react-bootstrap/Button";
import PageTitle from "../components/PageTitle";
import Form from "react-bootstrap/Form";
import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";
import FloatingLabel from "react-bootstrap/FloatingLabel";
import config from "../configData.json";
import "./Contact.scss";
export const Contact = () => {
const [validated, setValidated] = useState(false);
const form = useRef();
const sendEmail = (e) => {
e.preventDefault();
emailjs
.sendForm(
config.serviceId,
config.templateId,
"#contact-form",
config.publicKey
)
.then(
() => {
alert("Your message has been sent.");
setValidated(false);
e.target.reset();
},
(error) => {
alert("There was a problem sending your message.", error);
}
);
};
const handleSubmit = (event) => {
const form = event.currentTarget;
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
} else {
// alert("Message was sent!");
sendEmail(event);
}
setValidated(true);
};
return (
<>
<PageTitle title="Contact" />
<div className="container my-5">
<h2 className="mb-4">Leave a Message</h2>
<Form
noValidate
ref={form}
onSubmit={handleSubmit}
validated={validated}
id="contact-form"
>
<Row>
<Form.Group as={Col} md="12" controlId="nameValidation">
<FloatingLabel
controlId="floatingInput"
label="Name"
className="mb-3"
>
<Form.Control
type="text"
placeholder="Name"
name="user_name"
size="lg"
required
/>
<Form.Control.Feedback type="invalid">
Please enter your name.
</Form.Control.Feedback>
<Form.Control.Feedback>Looks good!</Form.Control.Feedback>
</FloatingLabel>
</Form.Group>
<Form.Group as={Col} md="12" controlId="emailValidation">
<FloatingLabel
controlId="floatingInput"
label="Email"
className="mb-3"
>
<Form.Control
type="email"
placeholder="name#example.com"
name="user_email"
size="lg"
required
/>
<Form.Control.Feedback type="invalid">
Please enter a valid email.
</Form.Control.Feedback>
<Form.Control.Feedback>Looks good!</Form.Control.Feedback>
</FloatingLabel>
</Form.Group>
<Form.Group as={Col} md="12" controlId="messageValidation">
<FloatingLabel
controlId="floatingInput"
label="Message"
className="mb-3"
>
<Form.Control
placeholder="Message"
name="user_message"
size="lg"
required
as="textarea"
rows={3}
/>
<Form.Control.Feedback type="invalid">
Please enter a message.
</Form.Control.Feedback>
<Form.Control.Feedback>Looks good!</Form.Control.Feedback>
</FloatingLabel>
</Form.Group>
</Row>
<Button
type="submit"
value="Send"
variant="primary"
size="lg"
className="mt-3 w-100"
>
SEND
</Button>
</Form>
</div>
</>
);
};
export default Contact;
Could you please share your HTML where you are getting this info?
Even required, you can make the type='email' required so it will validade for you.
if that still a problem (which should not be), then make an IF to validade for you.
Here is an example. You can use the function below to validade if it is an email. Send the email typed by the user via parameter.
const validateEmail = (email) => {
return String(email)
.toLowerCase()
.match(
/^(([^<>()[\]\\.,;:\s#"]+(\.[^<>()[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
);
};

I want to use email value in resetPass function. How can I do that?

I want to sent an email when reset password button is clicked. For that I have created a function named resetPass .I want to get email value in the function. How can I do that?
const Problem = () => {
const [
signInWithEmailAndPassword,
user,
loading,
error,
] = useSignInWithEmailAndPassword(auth, { sendEmailVerification: true });
//reset pass
const [sendPasswordResetEmail] = useSendPasswordResetEmail(
auth
);
//login button
//login button
const handleLogin = event => {
event.preventDefault();
const email = event?.target?.email?.value;
const password = event?.target?.password?.value;
signInWithEmailAndPassword(email, password)
}
//password reset
//password reset
const resetPass = () => {
//email: how to set email ???
sendPasswordResetEmail(auth, email)
.then(console.log('Email sent'))
}
return (
<div>
<Form onSubmit={handleLogin} className='w-50 mx-auto border p-5 m-5 bg-orange-200'>
<h2>Login</h2>
<Form.Group className="mb-3" controlId="formBasicEmail">
<Form.Label>Email address</Form.Label>
<Form.Control type="email" name='email' placeholder="Enter email" required />
<Form.Text className="text-muted">
We'll never share your email with anyone else.
</Form.Text>
</Form.Group>
<Form.Group className="mb-3" controlId="formBasicPassword">
<Form.Label>Password</Form.Label>
<Form.Control type="password" placeholder="Password" name='password' required />
</Form.Group>
<h6>Forgot password? <button onClick={resetPass}>Reset Password</button></h6>
<Button variant="primary" type="submit">
Submit
</Button> <br />
<div>
<SocialLogin></SocialLogin>
</div>
</Form>
</div>
);
};
export default Problem;

Default value not clearing off in react

I have a react jsx screen snippet as shown below:
import React,{useState} from "react";
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import ErrorCodes from './ErrorCodes.jsx';
export default function RegisterScreen()
{
const [Device_ID,setDevice_ID] = useState('');
const [Registerar_UserName,setRegisterar_UserName] = useState('');
const [Registerar_Email,setRegisterar_Email] = useState('');
const [Organisation_Name,setOrganisation_Name] = useState('');
const [Organisation_Email,setOrganisation_Email] = useState('');
const [Password,setPassword] = useState('');
const [ReenterPassword,setReenterPassword] = useState('');
const [Device_ID_Error,setDevice_ID_Error] = useState('');
const [Registerar_UserName_Error,setRegisterar_UserName_Error] = useState('');
const [Registerar_Email_Error,setRegisterar_Email_Error] = useState('');
const [Organisation_Name_Error,setOrganisation_Name_Error] = useState('');
const [Organisation_Email_Error,setOrganisation_Email_Error] = useState('');
const [ReenterPassword_Error,setReenterPassword_Error] = useState('');
return <Form className = "FormAligner">
<Form.Group controlId="formBasicEmail">
<Form.Label>Registered Device ID</Form.Label>
<Form.Control type="text"
onChange = {e=>{
setDevice_ID(e.target.value);
if(Device_ID.length!=12)
setDevice_ID_Error(ErrorCodes[5]);
else
setDevice_ID_Error(ErrorCodes[0]);
}}
placeholder="Device ID" value={Device_ID}/>
<Form.Text className="text-muted">
{Device_ID_Error}
</Form.Text>
</Form.Group>
<Form.Group controlId="formBasicEmail">
<Form.Label>Industry Name</Form.Label>
<Form.Control type="text" placeholder="Industry Name"
onChange={e=>{
setRegisterar_UserName(e.target.value);
if(Registerar_UserName.length===0)
setRegisterar_UserName_Error(ErrorCodes[1]);
else
setRegisterar_UserName_Error(ErrorCodes[0]);
}}
value={Registerar_UserName}/>
<Form.Text className="text-muted">
{Registerar_UserName_Error}
</Form.Text>
</Form.Group>
<Form.Group controlId="formBasicEmail">
<Form.Label>Industry Email</Form.Label>
<Form.Control type="email" placeholder="Industry Email"
value={Registerar_Email}
onChange={e=>{setRegisterar_Email(e.target.value)
let regex = /^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/;
if(regex.test(Registerar_Email))
setRegisterar_Email_Error(ErrorCodes[0]);
else
setRegisterar_Email_Error(ErrorCodes[4]);
}}/>
<Form.Text className="text-muted">
{Registerar_Email_Error}
</Form.Text>
</Form.Group>
<Form.Group controlId="formBasicEmail">
<Form.Label>Organisation Name</Form.Label>
<Form.Control type="text" placeholder="Organisation Name"
value={Organisation_Name}
onChange={e=>{setOrganisation_Name(e.target.value);
if(Organisation_Name.length===0)
setOrganisation_Name_Error(ErrorCodes[1]);
else
setOrganisation_Name_Error(ErrorCodes[0]);
}}/>
<Form.Text className="text-muted">
{Organisation_Name_Error}
</Form.Text>
</Form.Group>
<Form.Group controlId="formBasicEmail">
<Form.Label>Industry Email</Form.Label>
<Form.Control type="email" placeholder="Industry Email"
value={Organisation_Email}
onChange={e=>{
setOrganisation_Email(e.target.value);
let regex = /^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/;
if(regex.test(Organisation_Email))
setOrganisation_Email_Error(ErrorCodes[0]);
else
setOrganisation_Email_Error(ErrorCodes[4]);
}}/>
<Form.Text className="text-muted">
{Organisation_Email_Error}
</Form.Text>
</Form.Group>
<Form.Group controlId="formBasicPassword">
<Form.Label>Password</Form.Label>
<Form.Control type="password" placeholder="Enter Password"
value={Password}
onChange={e=>setPassword(e.target.value)}/>
</Form.Group>
<Form.Group controlId="formBasicPassword">
<Form.Label>Re-enter Password</Form.Label>
<Form.Control type="password" placeholder="Enter Password"
value={ReenterPassword}
onChange={e=>{
setReenterPassword(e.target.value);
if(ReenterPassword!=Password)
{
setReenterPassword_Error(ErrorCodes[6]);
}
else
setReenterPassword_Error(ErrorCodes[0]);
}}/>
<Form.Text className="text-muted">
{ReenterPassword_Error}
</Form.Text>
</Form.Group>
<Button variant="primary" className="Submit-Button" type="submit"
onClick={async(event)=>{
event.preventDefault();
const JSONString = {Device_ID,Registerar_UserName,Registerar_Email,Organisation_Name,Organisation_Email,Password,ReenterPassword};
console.log(JSON.stringify(JSONString));
const response = await fetch('http://localhost:5000/register',{
method: 'POST',
headers:{
'Content-Type':'application/json'
},
body:JSON.stringify(JSONString)
});
if(response.ok){
console.log("Response recieved");
}
}}>
Register
</Button>
</Form>
}
Here's how it looks:
For some reason, I don't know but the fields Industry Email and password always comes pre-filled. I tried to do so many things but not able to get why is this happening by default when other fields are empty.
Do I add some preventDefault function to avoid default actions? I know there are some unconventional ways to stop this from happening but I want to know the core concept to why is this even happening at the first place when I haven't done any such thing.
It seems like your browser is autocompleting those fields, you should check that and try to use autocomplete="off" in your inputs or your form to prevent that behavior.
I would assume that since those fields are set to type "email" and "password", its likely that your browser is auto filling these fields for you after having clicked yes on the "remember credentials" dialog. You can undo this by removing them in your browser credential manager.

Why isn't the Formik `touched` property being populated?

I'm making a form with React, Formik, react-bootstrap, and yup for validation. I am trying to display validation errors, but the touched property is not being populated with the fields.
const schema = yup.object({
name: yup.string().required(),
email: yup
.string()
.email()
.required(),
});
const ChildForm = props => {
const { child: { name = '', email = '' } = {} } = props;
const submitHandler = ({name, email}) => console.log(name, email);
return (
<Formik
validationSchema={schema}
onSubmit={submitHandler}
initialValues={{ name, email }}
render={({ handleSubmit, handleChange, values, touched, errors }) =>
{
console.log('touched: ', touched);
return (
<Form noValidate className="mt-4" onSubmit={handleSubmit}>
<Form.Row>
<Form.Group as={Col} controlId="name">
<Form.Label>Full Name</Form.Label>
<Form.Control
name="name"
required
value={values.name}
onChange={handleChange}
isValid={touched.name && !errors.name}
isInvalid={touched.name && errors.name}
type="text"
placeholder="Your child's name"
/>
<Form.Control.Feedback>Looks good!</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">
{errors.name || 'Please enter your child\'s name'}
</Form.Control.Feedback>
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col} controlId="email">
<Form.Label>Email Address</Form.Label>
<Form.Control
name="email"
required
value={values.email}
onChange={handleChange}
isValid={touched.email && !errors.email}
isInvalid={touched.email && errors.email}
type="text"
placeholder="Your child's email address"
/>
<Form.Control.Feedback>
No spam, we promise!
</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">
{errors.email || 'Please enter a valid email address'}
</Form.Control.Feedback>
</Form.Group>
</Form.Row>
<Form.Row className="float-right">
<Button variant="success" onClick={handleSubmit}>
<Icon icon={faSave} />
Submit
</Button>
</Form.Row>
</Form>
);
}}
/>
);
}
What am I doing wrong here? The console.log(touched) always shows an empty object.
#djheru Your solution is correct because Formik sets touched flags on blur event instead of on change. Here is Formik author comment about this:
You have to call Formiks handleBlur to notify Formik that blur event has been triggered - so yes, these handlers are needed.
I got it working by accessing the handleBlur function that's passed in the render function argument, and adding that as an onBlur handler for each of the form elements. Not sure if that's needed because I'm using react-bootstrap form components, but the react-bootstrap docs have a Formik example, but the touched object was not getting updated.
(
<Formik
validationSchema={schema}
onSubmit={submitForm}
initialValues={{ name, email }}
render={({
handleSubmit,
handleChange,
handleBlur, // handler for onBlur event of form elements
values,
touched,
errors,
}) => {
return (
<Form noValidate className="mt-4" onSubmit={handleSubmit}>
<Form.Row>
<Form.Group as={Col} controlId="nameControl">
<Form.Label>Full Name</Form.Label>
<Form.Control
name="name"
required
value={values.name}
onChange={handleChange}
onBlur={handleBlur} // This apparently updates `touched`?
isValid={touched.name && !errors.name}
isInvalid={touched.name && errors.name}
type="text"
placeholder="Your child's name"
/>
<Form.Control.Feedback>Looks good!</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">
{errors.name || 'Please enter your child\'s name'}
</Form.Control.Feedback>
</Form.Group>
</Form.Row>

Resources