I'm trying to add a react-bootstrap alert to my Formik form so that the handleSubmit includes an alert to the user that the form has submitted.
I have used the react-bootstrap documented form of Alert, however I had to change the last line because that seems not to work (the error says that I haven't exported anything if I use the react-bootstrap documented form of alert.
My alert is:
import React from 'react';
import {
Alert,
Button,
} from "react-bootstrap";
class AlertDismissible extends React.Component {
constructor(props) {
super(props);
this.state = { show: true };
}
render() {
const handleHide = () => this.setState({ show: false });
const handleShow = () => this.setState({ show: true });
return (
<>
<Alert show={this.state.show} variant="light">
<Alert.Heading>Thanks for your interest </Alert.Heading>
<p>
We'll be in touch with login details shortly.
</p>
<hr />
<div className="d-flex justify-content-end">
<Button onClick={handleHide} variant="outline-success">
Close
</Button>
</div>
</Alert>
{!this.state.show && <Button onClick={handleShow}>Show Alert</Button>}
</>
);
}
}
export default AlertDismissible;
The documentation shows a final line as:
render(<AlertDismissible />);
If I try to use that, an error message saying that render is not defined, and that nothing has been exported appears. So - I replaced that line with my final line.
Then, in my form I have:
handleSubmit = (formState, { resetForm }) => {
// Now, you're getting form state here!
const payload = {
...formState,
role: formState.role.value,
createdAt: firebase.firestore.FieldValue.serverTimestamp()
}
console.log("formvalues", payload);
fsDB
.collection("register")
.add(payload)
.then(docRef => {
resetForm(initialValues);
})
.then => {<AlertDismissible />}
.catch(error => {
console.error("Error adding document: ", error);
});
}
I don't actually know how to get the alert to work (the then statement above is a guess - I can't find any examples. This guess gives an error that says:
Parsing error: Unexpected token, expected ";"
I've tried adding ";" in every place I can think to put one, but it keeps generating errors.
If I try it like this:
.then(<AlertDismissible />)
I get no errors and the form submits, but the alert is not displayed.
Does anyone know how to call a react-bootstrap alert in the handle submit function?
Submit button has:
<Button
variant="outline-primary"
type="submit"
style={style3}
id="submitRegistration"
onClick={handleSubmit}
disabled={!dirty || isSubmitting}>
Register
</Button>
The onSubmit has:
onSubmit={
this.handleSubmit
}
My entire form looks like this:
import React from 'react';
import { Link } from "react-router-dom";
import { Formik, Form, Field, ErrorMessage, withFormik } from "formik";
import * as Yup from "yup";
import Select from "react-select";
import { fsDB, firebase, settings } from "../../firebase";
import Temporarynav from '../navigation/Temporarynav.jsx';
import Demo from '../landing/Demo.jsx';
import Footer from '../footer/Footer.jsx';
import "./preregister/Form.css";
import AlertDismissible from '../auth/preregister/Alert';
import {
Badge,
Button,
Col,
ComponentClass,
Feedback,
FormControl,
FormGroup,
Table,
Row,
Container
} from "react-bootstrap";
import Alert from 'react-bootstrap/Alert';
const style1 = {
width: "60%",
margin: "auto"
};
const style2 = {
paddingTop: "2em"
};
const style3 = {
marginRight: "2em"
};
const initialValues = {
firstName: "",
lastName: "",
email: "",
role: "",
consent: false,
createdAt: ''
}
class PreregForm extends React.Component {
// constructor(props) {
// super(props);
// // the flag isFormDone will control where you will show the Alert component
// this.state = {
// isFormDone: false
// };
// }
handleSubmit = (formState, { resetForm }) => {
// Now, you're getting form state here!
const payload = {
...formState,
role: formState.role.value,
createdAt: firebase.firestore.FieldValue.serverTimestamp()
}
console.log("formvalues", payload);
fsDB
.collection("preregistrations")
.add(payload)
.then(docRef => {
resetForm(initialValues);
})
.then(() => {
// Here is where you flag your form completion and allow the alert to be shown.
// this.setState((prevState) => {...prevState, isFormDone: true});
.catch(error => {
console.error("Error adding document: ", error);
});
}
render() {
const options = [
{ value: "academic", label: "Academic Researcher" },
{ value: "student", label: "Student (inc PhD)" },
]
// const {isFormDone} = this.state;
return(
<Formik
initialValues={initialValues}
validationSchema={Yup.object().shape({
firstName: Yup.string().required("First Name is required"),
lastName: Yup.string().required("Last Name is required"),
email: Yup.string()
.email("Email is invalid")
.required("Email is required"),
role: Yup.string().nullable().required(
"It will help us get started if we know a little about your background"
),
consent: Yup.boolean().oneOf(
[true],
"You must accept the Terms of Use and Privacy Policy"
)
})}
onSubmit={
this.handleSubmit
}
render={({
errors,
status,
touched,
setFieldValue,
setFieldTouched,
handleSubmit,
isSubmitting,
dirty,
values
}) => {
return (
<div>
<Temporarynav />
<Form style={style1}>
<h1 style={style2}>Get Started</h1>
<p>
We're almost ready to open this up to the research community. By
registering now, you'll be first in line when the doors open.
</p>
<div className="form-group">
<label htmlFor="firstName">First Name</label>
<Field
name="firstName"
type="text"
className={
"form-control" +
(errors.firstName && touched.firstName ? " is-invalid" : "")
}
/>
<ErrorMessage
name="firstName"
component="div"
className="invalid-feedback"
/>
</div>
<div className="form-group">
<label htmlFor="lastName">Last Name</label>
<Field
name="lastName"
type="text"
className={
"form-control" +
(errors.lastName && touched.lastName ? " is-invalid" : "")
}
/>
<ErrorMessage
name="lastName"
component="div"
className="invalid-feedback"
/>
</div>
<div className="form-group">
<label htmlFor="email">Email</label>
<Field
name="email"
type="text"
placeholder="Please use your work email address"
className={
"form-control" +
(errors.email && touched.email ? " is-invalid" : "")
}
/>
<ErrorMessage
name="email"
component="div"
className="invalid-feedback"
/>
</div>
<div className="form-group">
<label htmlFor="role">
Which role best describes yours?
</label>
<Select
key={`my_unique_select_keyrole`}
name="role"
className={
"react-select-container" +
(errors.role && touched.role ? " is-invalid" : "")
}
classNamePrefix="react-select"
value={values.role}
onChange={selectedOptions => {
// Setting field value - name of the field and values chosen.
setFieldValue("role", selectedOptions)}
}
onBlur={setFieldTouched}
options={options}
/>
{errors.role && touched.role &&
<ErrorMessage
name="role"
component="div"
className="invalid-feedback d-block"
/>}
</div>
<div className="form-group">
<div className="checkbox-wrapper">
<Field
name="consent"
type="checkbox"
checked={values.consent}
className={
"checkbox" +
(errors.consent && touched.consent ? " is-invalid" : "")
}
/>
<label htmlFor="consent" className="checkbox_label_wrapper">
You must accept the{" "}
<Link className="links" to={"/Terms"}>
Terms of Use
</Link>{" "}
and{" "}
<Link className="links" to={"/Privacy"}>
Privacy Policy
</Link>
</label>
</div>
{errors.consent && touched.consent &&
<ErrorMessage
name="consent"
component="div"
className="invalid-feedback d-block"
/>
}
</div>
<div className="form-group">
<Button
variant="outline-primary"
type="submit"
style={style3}
id="submitRegistration"
onClick={handleSubmit}
disabled={!dirty || isSubmitting}
>
Register
</Button>
</div>
</Form>
<Demo />
<Footer />
</div>
);
}
}
/>
)
}
}
export default PreregForm;
Next attempt
When I try Julius solution, the alert appears, but as a footer beneath the form - not as a popup alert.
Use state and conditional rendering. Instead of returning a component set state to a variable, in your render use conditional rendering to check if the value is true.
handleSubmit = (formState, { resetForm }) => {
// Now, you're getting form state here!
const payload = {
...formState,
role: formState.role.value,
createdAt: firebase.firestore.FieldValue.serverTimestamp()
};
console.log('formvalues', payload);
fsDB
.collection('register')
.add(payload)
.then(docRef => {
resetForm(initialValues);
})
.then(e => this.setState({ alert: true }))
.catch(error => {
console.error('Error adding document: ', error);
});
};
In your render
render() {
...
return(
....
{this.state.alert && <AlertDismissible />}
...
)
}
Example Demo
Complete form
import React from 'react';
import { Link } from 'react-router-dom';
import { Formik, Form, Field, ErrorMessage, withFormik } from 'formik';
import * as Yup from 'yup';
import Select from 'react-select';
import { fsDB, firebase, settings } from '../../firebase';
import Temporarynav from '../navigation/Temporarynav.jsx';
import Demo from '../landing/Demo.jsx';
import Footer from '../footer/Footer.jsx';
import './preregister/Form.css';
import AlertDismissible from '../auth/preregister/Alert';
import {
Badge,
Button,
Col,
ComponentClass,
Feedback,
FormControl,
FormGroup,
Table,
Row,
Container
} from 'react-bootstrap';
import Alert from 'react-bootstrap/Alert';
const style1 = {
width: '60%',
margin: 'auto'
};
const style2 = {
paddingTop: '2em'
};
const style3 = {
marginRight: '2em'
};
const initialValues = {
firstName: '',
lastName: '',
email: '',
role: '',
consent: false,
createdAt: ''
};
class PreregForm extends React.Component {
constructor(props) {
super(props);
// the flag isFormDone will control where you will show the Alert component
this.state = {
showAlert: false
};
}
handleSubmit = (formState, { resetForm }) => {
// Now, you're getting form state here!
const payload = {
...formState,
role: formState.role.value,
createdAt: firebase.firestore.FieldValue.serverTimestamp()
};
console.log('formvalues', payload);
fsDB
.collection('preregistrations')
.add(payload)
.then(docRef => {
resetForm(initialValues);
})
.then(e => this.setState({ showAlert: true }))
.catch(error => {
console.error('Error adding document: ', error);
});
};
render() {
const options = [
{ value: 'academic', label: 'Academic Researcher' },
{ value: 'student', label: 'Student (inc PhD)' }
];
// const {isFormDone} = this.state;
return (
<div>
{!this.state.showAlert ? (
<div>
<Formik
initialValues={initialValues}
validationSchema={Yup.object().shape({
firstName: Yup.string().required('First Name is required'),
lastName: Yup.string().required('Last Name is required'),
email: Yup.string()
.email('Email is invalid')
.required('Email is required'),
role: Yup.string()
.nullable()
.required(
'It will help us get started if we know a little about your background'
),
consent: Yup.boolean().oneOf(
[true],
'You must accept the Terms of Use and Privacy Policy'
)
})}
onSubmit={this.handleSubmit}
render={({
errors,
status,
touched,
setFieldValue,
setFieldTouched,
handleSubmit,
isSubmitting,
dirty,
values
}) => {
return (
<div>
<Temporarynav />
<Form style={style1}>
<h1 style={style2}>Get Started</h1>
<p>
We're almost ready to open this up to the research
community. By registering now, you'll be first in line
when the doors open.
</p>
<div className="form-group">
<label htmlFor="firstName">First Name</label>
<Field
name="firstName"
type="text"
className={
'form-control' +
(errors.firstName && touched.firstName
? ' is-invalid'
: '')
}
/>
<ErrorMessage
name="firstName"
component="div"
className="invalid-feedback"
/>
</div>
<div className="form-group">
<label htmlFor="lastName">Last Name</label>
<Field
name="lastName"
type="text"
className={
'form-control' +
(errors.lastName && touched.lastName
? ' is-invalid'
: '')
}
/>
<ErrorMessage
name="lastName"
component="div"
className="invalid-feedback"
/>
</div>
<div className="form-group">
<label htmlFor="email">Email</label>
<Field
name="email"
type="text"
placeholder="Please use your work email address"
className={
'form-control' +
(errors.email && touched.email ? ' is-invalid' : '')
}
/>
<ErrorMessage
name="email"
component="div"
className="invalid-feedback"
/>
</div>
<div className="form-group">
<label htmlFor="role">
Which role best describes yours?
</label>
<Select
key={`my_unique_select_keyrole`}
name="role"
className={
'react-select-container' +
(errors.role && touched.role ? ' is-invalid' : '')
}
classNamePrefix="react-select"
value={values.role}
onChange={selectedOptions => {
// Setting field value - name of the field and values chosen.
setFieldValue('role', selectedOptions);
}}
onBlur={setFieldTouched}
options={options}
/>
{errors.role && touched.role && (
<ErrorMessage
name="role"
component="div"
className="invalid-feedback d-block"
/>
)}
</div>
<div className="form-group">
<div className="checkbox-wrapper">
<Field
name="consent"
type="checkbox"
checked={values.consent}
className={
'checkbox' +
(errors.consent && touched.consent
? ' is-invalid'
: '')
}
/>
<label
htmlFor="consent"
className="checkbox_label_wrapper"
>
You must accept the{' '}
<Link className="links" to={'/Terms'}>
Terms of Use
</Link>{' '}
and{' '}
<Link className="links" to={'/Privacy'}>
Privacy Policy
</Link>
</label>
</div>
{errors.consent && touched.consent && (
<ErrorMessage
name="consent"
component="div"
className="invalid-feedback d-block"
/>
)}
</div>
<div className="form-group">
<Button
variant="outline-primary"
type="submit"
style={style3}
id="submitRegistration"
onClick={handleSubmit}
disabled={!dirty || isSubmitting}
>
Register
</Button>
</div>
</Form>
<Demo />
<Footer />
</div>
);
}}
/>
</div>
) : (
<AlertDismissible />
)}
</div>
);
}
}
export default PreregForm;
You can't return a component like that within the then function.
You should manage a state flag that shows the alert based on the form completion.
Maybe you could share your whole component where you are handling the submit shown here so we can give you more help (will update the answer if you update the question).
But I think it would be something along the following lines:
class MyFormComponent extends React.Component {
constructor(props) {
super(props);
// the flag isFormDone will control where you will show the Alert component
this.state = {
isFormDone: false
};
}
/** Here your form handling as you posted */
handleSubmit = (formState, { resetForm }) => {
const payload = {
...formState,
role: formState.role.value,
createdAt: firebase.firestore.FieldValue.serverTimestamp()
}
console.log("formvalues", payload);
fsDB
.collection("register")
.add(payload)
.then(docRef => {
resetForm(initialValues);
})
.then(() => {
// Here is where you flag your form completion and allow the alert to be shown.
this.setState((prevState) => ({...prevState, isFormDone: true}));
})
.catch(error => {
console.error("Error adding document: ", error);
});
}
render() {
const {isFormDone} = this.state;
// this will only render the Alert when you complete your form submission
const alert = isFormDone ? <AlertDismissible/> : null;
return(
{/* syntax sugar for React Fragment */}
<>
{/* if it is null, it won't render anything */}
{alert}
{/* Your Form here with your handler */}
{/* ... */}
</>
);
}
}
Related
On the code below there is a text input that shows an error message when:
type invalid email (or leave blank) and hit enter
type invalid email (or leave blank) and blur (or clicking outside)
import { ErrorMessage, Field, Form, Formik } from 'formik';
import React from 'react';
import * as Yup from 'yup';
const TextInput = ({ value, handleChange }) => (
<input
type="text"
value={value}
onChange={(e) => handleChange(e.target.value)}
/>
);
export default () => {
return (
<Formik
initialValues={{
email: '',
}}
validationSchema={Yup.object().shape({
email: Yup.string()
.required('Email is required.')
.email('Email is invalid.'),
})}
onSubmit={(values, { setSubmitting }) => {
console.log(values);
setSubmitting(false);
}}
enableReinitialize
>
{({ setFieldValue }) => (
<Form>
<div>
<Field
type="text"
name="email"
onChange={({ target: { value } }) => {
console.log(value);
setFieldValue('email', value);
}}
/>
<ErrorMessage name="email">
{(error) => <div style={{ color: '#f00' }}>{error}</div>}
</ErrorMessage>
</div>
<input type="submit" value="Submit" />
</Form>
)}
</Formik>
);
};
Now I want to keep the same behavior when doing the following change:
from
<Field
type="text"
name="email"
onChange={({target:{value}}) => {
console.log(value);
setFieldValue("email", value);
}}
/>
to:
<Field
component={TextInput}
name="email"
handleChange={(value) => {
console.log(value);
setFieldValue("email", value);
}}
/>
I mean, basically I'm changing from: type="text" to component={TextInput} which is basically a text input as you can see above.
After doing that change the error happens when:
[SUCCESS] type invalid email (or leave blank) and hit enter
but not when:
[FAIL] type invalid email (or leave blank) and blur (or clicking outside)
Do you know how can I get the error displayed on the second situation?
I'm looking for a standard way to do it and not tricky workarounds.
Here you have a StackBlitz you can play with and if you want you can post your forked one.
Thanks!
The way I handle this is to pass name and id between the component and wrapper, otherwise, Formik has no relationship and therefore no knowledge of what to error.
Here's a fork
import { ErrorMessage, Field, Form, Formik } from 'formik';
import React from 'react';
import * as Yup from 'yup';
const TextInput = ({ value, handleChange, name, id }) => (
<Field
id={id} // <- adding id
name={name} // <- adding name
type="text"
value={value}
onChange={handleChange} // You don't need to capture the event, you've already done the work in the wrapper
/>
);
export default () => {
return (
<Formik
initialValues={{
email: '',
}}
validationSchema={Yup.object().shape({
email: Yup.string()
.required('Email is required.')
.email('Email is invalid.'),
})}
onSubmit={(values, { setSubmitting }) => {
console.log(values);
setSubmitting(false);
}}
enableReinitialize
>
{({ setFieldValue }) => (
<Form>
<div>
<Field
component={TextInput}
id="email" // <- id
name="email" // <- name
handleChange={e => {
const value = e.target.value;
console.log(value);
setFieldValue('email', value);
}}
/>
<ErrorMessage name="email">
{(error) => <div style={{ color: '#f00' }}>{error}</div>}
</ErrorMessage>
</div>
<input type="submit" value="Submit" />
</Form>
)}
</Formik>
);
};
I have the following code which you can find here:
https://stackblitz.com/edit/react-d2fadr?file=src%2FApp.js
import { ErrorMessage, Field, Form, Formik } from 'formik';
import React from 'react';
import { Button } from 'react-bootstrap';
import * as Yup from 'yup';
let fieldName = 'hexColor';
const TextInput = ({ field, value, placeholder, handleChange }) => {
value = (field && field.value) || value || '';
placeholder = placeholder || '';
return (
<input
type="text"
placeholder={placeholder}
onChange={(e) => handleChange(e.target.value)}
value={value}
/>
);
};
export default () => {
const onSubmit = (values, { setSubmitting }) => {
console.log(values);
setSubmitting(false);
};
return (
<Formik
initialValues={{ [fieldName]: 'ff0000' }}
validationSchema={Yup.object({
hexColor: Yup.string().test(
fieldName,
'The Hex Color is Wrong.',
(value) => {
return /^[0-9a-f]{6}$/.test(value);
}
),
})}
onSubmit={onSubmit}
enableReinitialize
>
{(formik) => {
const handleChange = (value) => {
value = value.replace(/[^0-9a-f]/g, '');
formik.setFieldValue(fieldName, value);
};
return (
<Form>
<div>
<Field
component={TextInput}
name={fieldName}
placeholder="Hex Color"
handleChange={handleChange}
/>
<ErrorMessage name={fieldName} />
</div>
<Button
type="submit"
disabled={!formik.isValid || formik.isSubmitting}
>
Submit
</Button>
</Form>
);
}}
</Formik>
);
};
I want to know if is it any way to render the ErrorMessage element automatically?
The error message should be shown somewhere around the input text.
If you know how, you can fork the StackBlitz above with your suggestion.
Thanks!
Don't really know why ErroMessage is not rendering before you submit your form once but you can replace the line <ErrorMessage name={fieldName} /> by {formik.errors[fieldName]} to make it works
import { ErrorMessage, Field, Form, Formik } from 'formik';
import React from 'react';
import { Button } from 'react-bootstrap';
import * as Yup from 'yup';
let fieldName = 'hexColor';
const TextInput = ({ field, value, placeholder, handleChange }) => {
value = (field && field.value) || value || '';
placeholder = placeholder || '';
return (
<input
type="text"
placeholder={placeholder}
onChange={(e) => handleChange(e.target.value)}
value={value}
/>
);
};
export default () => {
const onSubmit = (values, { setSubmitting }) => {
console.log(values);
setSubmitting(false);
};
return (
<Formik
initialValues={{ [fieldName]: 'ff0000' }}
validationSchema={Yup.object({
hexColor: Yup.string().test(
fieldName,
'The Hex Color is Wrong.',
(value) => {
return /^[0-9a-f]{6}$/.test(value);
}
),
})}
onSubmit={onSubmit}
enableReinitialize
>
{(formik) => {
const handleChange = (value) => {
value = value.replace(/[^0-9a-f]/g, '');
formik.setFieldValue(fieldName, value);
};
return (
<Form>
<div>
<Field
component={TextInput}
name={fieldName}
placeholder="Hex Color"
handleChange={handleChange}
/>
{formik.errors[fieldName]}
</div>
<Button
type="submit"
disabled={!formik.isValid || formik.isSubmitting}
>
Submit
</Button>
</Form>
);
}}
</Formik>
);
};
the issue is validation schema. When I changed 6
return /^[0-9a-f]{6}$/.test(value);
to 3
return /^[0-9a-f]{3}$/.test(value);
and submitted with the initial value, ErrorMessage component is rendered
To reach your goal, I changed your code as below:
Since Formik's default component is Input, I deleted your TextInput component as there was nothing special in your component and handleChange function.
<Field name="hexColor" placeholder="Hex Color" onChange={(e) => handleChange(e.target.value)}/>
<ErrorMessage name="hexColor" />
As in mentioned in this answer, I changed your submit button condition to determine whether the button is disabled or not:
<Button type="submit" disabled={Object.keys(errors).length}>
Submit
</Button>
You can view my entire solution here.
Edit
If you want to keep your component, you should pass props as you might be missing something important, e.g. onChange,onBlur etc.
const TextInput = ({ field, ...props }) => {
return (
<input
{...field} {...props}
// ... your custom things
/>
);
};
<Field
component={TextInput}
name={fieldName}
placeholder="Hex Color"
onChange={(e) => handleChange(e.target.value)}
/>
Solution 2
I have successfully written a Login component in React using plain Javascript. I would now like to convert this component to TypeScript. I have defined some types and also threw in some "any" just to initially compile. Unfortunately, the onClick parameter in my submit button throws the following error:
TS2322: Type '(e?: FormEvent | undefined) => void' is not assignable to type '(event: MouseEvent<HTMLElement, MouseEvent>)
=> void'.
Here is the relevant code:
class Login extends React.Component<LoginProps, LoginState> {
constructor(props) {
super(props);
this.login = this.login.bind(this);
}
async login(values) {
const user = {
email: values.email,
password: values.password,
};
const query = `mutation userLogin(
$user: UserLoginInputs!
) {
userLogin(
user: $user
) {
token
}
}`;
const data: any = await graphQLFetch(query, { user });
if (data && data.userLogin.token) {
const decoded: any = jwt.decode(data.userLogin.token);
const { onUserChange } = this.props;
onUserChange({ loggedIn: true, givenName: decoded.givenName });
const { history } = this.props;
history.push('/');
}
}
render() {
return (
<Formik
onSubmit={this.login}
validationSchema={loginSchema}
initialValues={{
email: '',
password: '',
}}
>
{({
handleSubmit,
handleChange,
values,
}) => (
<Card>
<Card.Body>
<Form>
<Form.Group>
<Form.Label>E-mail</Form.Label>
<Form.Control
name="email"
value={values.email}
onChange={handleChange}
/>
</Form.Group>
<Form.Group>
<Form.Label>Password</Form.Label>
<Form.Control
name="password"
value={values.password}
onChange={handleChange}
/>
</Form.Group>
</Form>
<Button
type="button"
variant="primary"
onClick={handleSubmit}
>
Submit
</Button>
</Card.Body>
</Card>
)}
</Formik>
);
}
}
I'm new to TypeScript and don't fully understand why an error occurs regarding e versus event when the login function does not explicitly reference either of those. Can someone please help me assign types to my handleSubmit function (aka login)?
I hope that example could help you to resolve your issue
import { Field, Form, Formik } from 'formik';
import * as React from 'react';
import './style.css';
interface MyFormValues {
firstName: string;
}
export default function App() {
const initialValues: MyFormValues = { firstName: '' };
const getSubmitHandler =
() =>
(values, formik) => {
console.log({ values, formik });
alert(JSON.stringify(values, null, 2));
formik.setSubmitting(false);
};
return (
<div>
<Formik
initialValues={initialValues}
onSubmit={getSubmitHandler()}
>
{(formik) => (
<Form>
<label htmlFor="firstName">First Name</label>
<Field id="firstName" name="firstName" placeholder="First Name" />
<button
type="button"
onClick={(event) =>
getSubmitHandler()(formik.values, formik)
}
>
Submit
</button>
</Form>
)}
</Formik>
</div>
);
}
I am currently stuck on checking unique username in the sign up(on firebase) in react. My goal is that
1. when the user input the username, the function checkNameAvalaible go to the firebase database and check if the name exists.
2.if the name exists set the state of usernameavalaible to false, and set it to true if name works
3.disable the submit button of the sign up if the usernameavalaible is false.
4.If the username is taken, use helper text in textfield to alert the current user.
I am stuck with the if statement, I tried a few times and it is not working here is my code
import React, { Component } from "react";
import { Link } from "react-router-dom";
import { auth } from "../firebase";
import { db} from "../firebase";
import "./auth.css";
import * as routes from "../constants/routes";
import { SignInLink } from "./SignIn";
import TextField from "#material-ui/core/TextField";
import Button from "#material-ui/core/Button";
const SignUpPage = ({ history }) => (
<div align="center" className="SignUpBox">
<h1>SignUp</h1>
<SignUpForm history={history} />
<SignInLink />
</div>
);
const INITIAL_STATE = {
username: "",
name: "",
email: "",
passwordOne: "",
passwordTwo: "",
error: null
};
const byPropKey = (propertyName, value) => () => ({
[propertyName]: value
});
class SignUpForm extends Component {
state = { ...INITIAL_STATE };
// checkPassword() {
// if(!this.state.passwordOne || this.state.passwordOne !== this.state.passwordTwo){
// this.setState({error:"passwords do not match"});
// }
// else {
// this.setState({error:null});
// }
// }
onSubmit = event => {
event.preventDefault();
const { email, passwordOne, name, username } = this.state;
auth
.doCreateUserWithEmailAndPassword(
email,
passwordOne,
name,
username
)
.then(authUser => {
this.setState(() => ({ ...INITIAL_STATE }));
this.props.history.push(routes.HOME);
})
.catch(error => {
this.setState(byPropKey("error", error));
});
};
render() {
var usernameAvalaible
const {
username,
name,
email,
passwordOne,
passwordTwo,
error
} = this.state;
checkNameAvalaible(){
if (db.doc(`/users/${username}`).get().exists){
this.setState{usernameAvalaible:false};
}
else{
this.setState{usernameAvalaible:true};
}
};
const isInvalid =
passwordOne !== passwordTwo ||
passwordOne === "" ||
email === "" ||
name === "" ||
username === ""|| usernameAvalaible===false;
return (
<form onSubmit={this.onSubmit}>
<TextField
name="Username"
id="standard-secondary"
label="User name"
color="primary"
value={username}
onChange={event =>
this.setState(byPropKey("username", event.target.value))
}
type="text"
/>
<TextField
name="name"
value={name}
id="standard-secondary"
label="Full name"
color="primary"
onChange={event =>
this.setState(byPropKey("name", event.target.value))
}
type="text"
/>
<TextField
name="email"
value={email}
id="standard-secondary"
label="Email Address"
color="primary"
onChange={event =>
this.setState(byPropKey("email", event.target.value))
}
type="email"
/>
<TextField
name="password"
value={passwordOne}
id="standard-secondary"
label="Password"
color="primary"
onChange={event =>
this.setState(byPropKey("passwordOne", event.target.value))
}
type="password"
/>
<TextField
name="ConfirmPassword"
value={passwordTwo}
id="standard-secondary"
label="Comfirm Password"
color="primary"
onChange={event =>
this.setState(byPropKey("passwordTwo", event.target.value))
}
type="password"
/>
<br />
<br />
<Button
type="submit"
disabled={isInvalid}
variant="contained"
color="primary"
>
Sign Up
</Button>
{error && <p style={{ color: "red" }}>{error.message}</p>}
</form>
);
}
}
const SignUpLink = () => (
<p>
Don't have an account? <Link to={routes.SIGN_UP}>Sign Up</Link>
</p>
);
export default SignUpPage;
export { SignUpForm, SignUpLink };
I'm using Formik to create a generic contact form in react. I am getting data from my api and attempting to call Formik's setStatus to generate a message to show that the form has been submitted successfully.
For whatever reason the Status state never gets updated to reflect what I put in setStatus.
Here's my code:
import { Formik, Form, useField } from "formik";
import * as Yup from "yup";
import axios from "axios";
import Button from "components/shared/button";
import "styles/contact/form.scss";
const handleSubmit = (values, actions) => {
axios.post("http://localhost:5000/sendemail/", values).then(res => {
actions.setSubmitting(false);
actions.setStatus = {
message: res.data.message
};
setTimeout(() => {
actions.resetForm();
}, 3000);
});
};
const FormField = ({ label, tagName, ...props }) => {
const [field, meta] = useField(props);
const errorClass = meta.touched && meta.error ? "error" : "";
const TagName = tagName;
return (
<>
<label htmlFor={props.id || props.name}>
{label}
{meta.touched && meta.error ? (
<span className="error">{meta.error}</span>
) : null}
</label>
<TagName
className={`form-control ${errorClass}`}
{...field}
{...props}
/>
</>
);
};
const ContactForm = () => (
<Formik
initialValues={{ name: "", email: "", msg: "" }}
validationSchema={Yup.object({
name: Yup.string().required("Required"),
email: Yup.string()
.email("Invalid email address")
.required("Required"),
msg: Yup.string().required("Required")
})}
onSubmit={handleSubmit}>
{({ isSubmitting, status }) => (
<Form className="contact-form">
<div className="row form-group">
<div className="col">
<FormField
label="Name"
name="name"
type="text"
tagName="input"
/>
</div>
<div className="col">
<FormField
label="Email"
name="email"
type="text"
tagName="input"
/>
</div>
</div>
<div className="form-group">
<FormField label="Message" name="msg" tagName="textarea" />
</div>
{status && status.message && (
<div className="message">{status.message}</div>
)}
<Button
id="formSubmit"
text="Send Message"
type="submit"
isSubmitting={isSubmitting}
/>
</Form>
)}
</Formik>
);
export default ContactForm;
Just before my submit button, it should show the <div class="message">Success message</div> after submitting the form. When I try to debug the value of Status is always "undefined".
Any one have a clue what I'm doing wrong here?
The reason it wasn't working is because I tried to set the value of setStatus equal to an object. What I should have done was used it as a method and pass the object as a parameter.
Like so:
actions.setStatus({message: res.data.message});
I feel silly for missing this simple mistake for so long.