I have a signup form inside a React functional component,
https://codesandbox.io/s/vigilant-robinson-g8y3n
The validator function returns a promise. If every field of the form is correct, it returns a resolve else a reject. If the validator returns a resolve then I perform an API Call ( Here I have just console logged "API Request Sent" ) else I console log the errors.
Even If I perform an invalid form input ( especially the email ), the API Request is still sent.
import React, { useState } from 'react';
import Button from '#material-ui/core/Button';
import { makeStyles } from '#material-ui/styles';
import TextField from '#material-ui/core/TextField';
const useStyles = makeStyles(() => ({
avatar: {
margin: '10px auto',
backgroundColor: 'green',
},
paper: {
width: '50vw',
margin: '1rem auto 3rem auto',
padding: '2rem',
display: 'flex',
flexDirection: 'column',
backgroundColor: 'blue',
backdropFilter: 'blur(10px)',
},
align: {
marginLeft: 'auto',
marginRight: 'auto',
},
field: {
width: '100%',
marginLeft: 'auto',
marginRight: 'auto',
marginTop: '15px',
},
form: {
width: '80%',
display: 'flex',
flexDirection: 'column',
margin: '1rem auto',
},
}));
export default function Signup() {
const classes = useStyles();
let [error, setError] = useState('');
let data = {};
function setData(event, key) {
data[key] = event.target.value;
}
function validator() {
console.log(data);
return new Promise((resolve, reject) => {
const exp = RegExp('w+#example.com').test(data.email);
console.log(exp);
if (!exp) {
setError('Email Not Valid! Please use Institute Email ID');
}
if (data.password !== data.password2) {
setError('Password Does Not Match!');
}
console.log(error);
if (error !== '') {
reject(error);
} else {
resolve('ok');
}
});
}
const onSignup = (e) => {
e.preventDefault();
validator()
.then(() => {
console.log('API Request Sent');
})
.catch(() => {
console.log('The errors are: '+ error)
});
};
return (
<form className={classes.form}>
<TextField
required
className={classes.field}
id="outlined-required"
label="User Name"
variant="outlined"
onChange={(event) => setData(event, 'username')}
/>
<TextField
required
type="email"
className={classes.field}
id="outlined-required"
label="Email"
variant="outlined"
onChange={(event) => {
setData(event, 'email');
}}
/>
<TextField
required
className={classes.field}
id="outlined-required"
label="First Name"
variant="outlined"
onChange={(event) => setData(event, 'first_name')}
/>
<TextField
required
className={classes.field}
id="outlined-required"
label="Last Name"
variant="outlined"
onChange={(event) => setData(event, 'last_name')}
/>
<TextField
required
type="password"
className={classes.field}
id="outlined-required"
label="Password"
variant="outlined"
onChange={(event) => setData(event, 'password')}
/>
<TextField
required
type="password"
className={classes.field}
id="outlined-required"
label="Confirm Password"
variant="outlined"
onChange={(event) => {
setData(event, 'password2');
}}
/>
<Button
className={classes.field}
color="primary"
variant="contained"
onClick={(e) => onSignup(e)}
>
Sign Up
</Button>
</form>
);
}
I ended up having to change a lot of your code. First off, you were using a plain object to handle your form state, instead of using useState.
let data = {};
function setData(event, key) {
data[key] = event.target.value;
}
I changed that to utilize useState where I also initialized all of the field values in the object right off the bat.
const [data, setDataForm] = useState({
username: "",
email: "",
first_name: "",
last_name: "",
password: "",
password2: ""
});
const setData = (e, key) => {
setDataForm((prev) => ({ ...prev, [key]: e.target.value }));
};
Not only that, you also weren't setting the value field on all of your input fields, which I added like so:
<TextField
required
className={classes.field}
id="outlined-required"
label="User Name"
variant="outlined"
onChange={(event) => setData(event, "username")}
value={data.username}
/>
For the last portion, specifically the error handling, I added a local err variable that handles recording what type of error it was. React state updates are asynchronous, and aren't guaranteed to finish before your code executes.
For example, where you do this:
if (!exp) {
setError('Email Not Valid! Please use Institute Email ID');
}
if (data.password !== data.password2) {
setError('Password Does Not Match!');
}
console.log(error);
if (error !== '') {
reject(error);
} else {
resolve('ok');
}
error might not be up to date when you attempt to reject it below. Therefore, using a local variable guarantees that you will always catch the error. At the very end you can then set the error to your error state.
Full code below (minus styles)
export default function Signup() {
const classes = useStyles();
let [error, setError] = useState("");
const [data, setDataForm] = useState({
username: "",
email: "",
first_name: "",
last_name: "",
password: "",
password2: ""
});
const setData = (e, key) => {
setDataForm((prev) => ({ ...prev, [key]: e.target.value }));
};
function validator() {
return new Promise((resolve, reject) => {
let err = "";
const exp = RegExp("w+#btech.nitdgp.ac.in|nitdgp.ac.in").test(data.email);
if (!exp) {
err = "Email Not Valid! Please use Institute Email ID";
} else if (data.password === "" || data.password !== data.password2) {
err = "Password Does Not Match!";
}
if (err !== "") {
setError(err);
reject(err);
} else {
resolve("ok");
}
});
}
const onSignup = (e) => {
e.preventDefault();
validator()
.then(() => {
console.log("API Request Sent");
})
.catch(() => {
console.log("The errors are: " + error);
});
};
return (
<form className={classes.form}>
<TextField
required
className={classes.field}
id="outlined-required"
label="User Name"
variant="outlined"
onChange={(event) => setData(event, "username")}
value={data.username}
/>
<TextField
required
type="email"
className={classes.field}
id="outlined-required"
label="Email"
variant="outlined"
onChange={(event) => {
setData(event, "email");
}}
value={data.email}
/>
<TextField
required
className={classes.field}
id="outlined-required"
label="First Name"
variant="outlined"
onChange={(event) => setData(event, "first_name")}
value={data.first_name}
/>
<TextField
required
className={classes.field}
id="outlined-required"
label="Last Name"
variant="outlined"
onChange={(event) => setData(event, "last_name")}
value={data.last_name}
/>
<TextField
required
type="password"
className={classes.field}
id="outlined-required"
label="Password"
variant="outlined"
onChange={(event) => setData(event, "password")}
value={data.password}
/>
<TextField
required
type="password"
className={classes.field}
id="outlined-required"
label="Confirm Password"
variant="outlined"
onChange={(event) => {
setData(event, "password2");
}}
value={data.password2}
/>
<Button
className={classes.field}
color="primary"
variant="contained"
onClick={(e) => onSignup(e)}
>
Sign Up
</Button>
</form>
);
}
Related
i am using react Mui for components ,not getting any errors in chrome inspector or terminal
how can i slove this
I get no errors from either eslint nor Chrome Inspector.
Submitting the form itself works as does the actual input field when it is located either in the render's return or while being imported as a separate component but not in how I have it coded below.
Why is this so?
Here is my code :
import React, { useState } from "react";
import {
Box,
Container,
Stack,
TextField,
Typography,
Button,
Divider,
} from "#mui/material";
import { styled } from "#mui/material/styles";
import { Link, useNavigate } from "react-router-dom";
const Register = () => {
const Curve = styled("div")(({ theme }) => ({
height: "35vh",
position: "absolute",
top: 0,
left: 0,
width: "100%",
background: `linear-gradient(120deg,${theme.palette.secondary.light},${theme.palette.secondary.main})`,
zIndex: -1,
borderBottomLeftRadius: "30px",
borderBottomRightRadius: "30px",
}));
const ProfileBox = styled(Box)(({ theme }) => ({
// margin: theme.spacing(18, 1),
background: theme.palette.primary.dark,
border: `solid 0.8px ${theme.palette.primary.light}`,
borderRadius: "10px",
padding: theme.spacing(3),
}));
const navigate = useNavigate();
const handleRegister = (e) => {
e.preventDefault();
navigate("/");
};
const [values, setValues] = useState({
name: "",
email: "",
password: "",
conpassword: "",
phone: "",
proffesion: "",
});
const [err, setErr] = useState({});
const [valid, setValid] = useState(false);
const handleChange = (e) => {
const { id, value } = e.target;
setValues(() => ({
[id]: value,
}));
setValid(() => true);
};
const validate = () => {
return setErr(checkErr());
function checkErr() {
const error = {};
if (values.name.length === 0) error.name = "Name nedded";
if (values.password.length === 0) error.password = "Password nedded";
if (!values.email.match(/^[^\s#]+#[^\s#]+\.[^\s#]+$/))
error.email = "Invalid email";
if (values.password.length < 6)
error.password = "Password must be longer than 6 charectors";
if (values.password !== values.conpassword)
error.conpassword = "Password doesn't match";
if (values.email.length === 0) error.email = "Email nedded";
if (values.phone.length === 0) error.phone = "Phone number nedded";
if (values.phone.length < 10) error.phone = "Invalid number";
if (values.proffesion.length === 0)
error.proffesion = "Proffesion nedded ex: lawyer ,student";
return error;
}
};
return (
<Container maxWidth="sm">
<Curve></Curve>
<Box
sx={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
height: "30vh",
alignItems: "center",
}}
>
<Typography variant="h6" algin="center">
Register
</Typography>
<Typography variant="caption" algin="center">
your data is secured with us
</Typography>
</Box>
<ProfileBox>
<Stack spacing={2}>
<TextField
variant="standard"
label="Name"
color="secondary"
value={values.name}
onChange={handleChange}
onBlur={validate}
id="name"
error={err.name}
helperText={err.name}
/>
<TextField
type="email"
variant="standard"
label="Email"
color="secondary"
id="email"
value={values.email}
onChange={handleChange}
onBlur={validate}
error={err.email}
helperText={err.email}
/>
<TextField
type="password"
variant="standard"
label="Password"
color="secondary"
value={values.password}
onChange={handleChange}
onBlur={validate}
id="password"
error={err.password}
helperText={err.password}
/>
<TextField
type="password"
variant="standard"
label="Conform password"
color="secondary"
value={values.conpassword}
onChange={handleChange}
onBlur={validate}
id="conpassword"
error={err.conpassword}
helperText={err.conpassword}
/>
<TextField
type="tel"
variant="standard"
label="Phone"
color="secondary"
value={values.phone}
onChange={handleChange}
onBlur={validate}
id="phone"
error={err.phone}
helperText={err.phone}
/>
<TextField
variant="standard"
label="Proffestion"
color="secondary"
value={values.proffesion}
onChange={handleChange}
onBlur={validate}
id="proffesion"
error={err.proffesion}
helperText={err.proffesion}
/>
<Button
variant="contained"
color="secondary"
sx={{
color: "primary.main",
}}
onClick={handleRegister}
>
Signup
</Button>
<Divider />
<Typography variant="caption" algin="center">
Allready have account{" "}
<span>
<Link to="/login" style={{ color: "var(--secondary)" }}>
Login
</Link>
</span>
</Typography>
</Stack>
</ProfileBox>
</Container>
);
};
export default Register;
Try to change your handleChange function like this:
const handleChange = (e) => {
const { id, value } = e.target;
setValues(() => (prevState => {
return {
...prevState,
[id]: value,
}
}));
setValid(true);
};
This is happening because you're creating components inside your Register component. This pattern is really bad for performance and prone to bugs exactly like your question.
On every type you're changing the state of the Register component, it re-renders itself, and re-creates Curve and ProfileBox from scratch, including their children (input fields). Which causes them to reset all their and their children's state, including focus.
You need to move Curve and ProfileBox outside of it, it will fix the issue.
const Curve = styled('div')(({ theme }) => ({
... // the same
}));
const ProfileBox = styled(Box)(({ theme }) => ({
... // the same
}));
const Register = () => {
this may not be able to be answered, but I am going to try to include as much detail as possible. I have this page called registrationForm.js, which in my routes.js is set to render with the path /signup. It does render the page, but it is blank. When I refresh the page, for a split second I see all the elements I'm supposed to, the input fields, the buttons, the images. But for some reason, it goes back to being fully white page.
Here is the entire registrationForm.js code!
import React from 'react'
import './RegistrationForm.css'
import { Alert, Button, Form, Input } from 'antd'
import { AuthorizationHome } from '../models'
import { withRouter } from 'react-router-dom'
import * as routes from '../routes'
import facades from '../facades'
import firebase from 'firebase'
import { useState } from "react";
import {createCheckoutSession} from '../Stripepayment/checkout'
let firestore = firebase.firestore()
const FormItem = Form.Item
// const [firstname, setFirstName] = useState("");
// const [lastname, setLastName] = useState("");
// const [companyname, setCompanyName] = useState("");
// const [accountStatus, setAccountStatus] = useState("");
var userIDStripeSubmit = ""
class RegistrationForm extends React.Component {
state = {
confirmDirty: false,
}
onSubmit = (event) => {
event.preventDefault();
this.props.form.validateFields((err, values) => {
if (err) return
const email = this.props.form.getFieldValue('email')
const passwordOne = this.props.form.getFieldValue('password1')
const firstName = this.props.form.getFieldValue('First Name')
const lastName = this.props.form.getFieldValue('Last Name')
const companyName = this.props.form.getFieldValue('Company Name')
const {
history,
} = this.props
AuthorizationHome.doCreateUserWithEmailAndPassword(email, passwordOne)
.then((authUser) => facades.userFacade().doCreateUser(authUser.user.uid, email))
// .then((authUser) => {
// history.push(createCheckoutSession(authUser.user.uid))
// })
.catch(error => {
this.setState({'error': error})
})
// adding into profiledata collection
// var userID = ""
firebase.auth().onAuthStateChanged((user) => {
if(user) {
// console.log("profile.js " + user.uid)
// userID = user.uid
// userID = user.uid
console.log(user.uid)
userIDStripeSubmit = user.uid
console.log("userid inside firebase auth is" + user.uid)
// var firstNameFromField =
// console.log("this props : " + this.props.form.getFieldValue('First Name'))
firestore.collection('profiledata').doc(user.uid).set({
firstname: firstName,
lastname: lastName,
companyname: companyName,
accountStatus: "inactive",
})
.catch(error => {
alert(error.message);
});
createCheckoutSession(user.uid)
}
})
// firestore collection query
// setFirstName("");
// setLastName("");
// setCompanyName("");
// setAccountStatus("")
})
}
handleConfirmBlur = (e) => {
const value = e.target.value;
this.setState({ confirmDirty: this.state.confirmDirty || !!value });
}
compareToFirstPassword = (rule, value, callback) => {
const form = this.props.form;
if (value && value !== form.getFieldValue('password1')) {
callback('Passwords do not match!');
} else {
callback();
}
}
validateToNextPassword = (rule, value, callback) => {
const form = this.props.form;
if (value && this.state.confirmDirty) {
form.validateFields(['password2'], { force: true });
}
callback();
}
render() {
const { getFieldDecorator } = this.props.form
const { error } = this.state
return (
<Form onSubmit={this.onSubmit} hideRequiredMark={true} className="registration-form" style={{ marginBottom: "0px" }}>
{ error && <Alert type="error" message={error.message}/> }
<FormItem label="Email" colon={false} style={{ marginBottom: "0px" }}>
{getFieldDecorator('email', {
rules: [{
type: 'email', message: 'Invalid email address',
}, {
required: true, message: 'Please input your email address',
}],
})(
<Input placeholder="Enter email" />
)}
</FormItem>
{/* */}
<FormItem label="First Name" colon={false} style={{ marginBottom: "0px" }}>
{getFieldDecorator('First Name', {
rules: [{
required: true, message: 'Please enter your First Name',
}],
})(
<Input type="text" placeholder="Enter Your First Name"/>
)}
</FormItem>
<FormItem label="Last Name" colon={false} style={{ marginBottom: "0px" }}>
{getFieldDecorator('Last Name', {
rules: [{
required: true, message: 'Please enter your Last Name',
}],
})(
<Input type="text" placeholder="Enter Your Last Name"/>
)}
</FormItem>
<FormItem label="Company Name" colon={false} style={{ marginBottom: "0px" }}>
{getFieldDecorator('Company Name', {
rules: [{
required: true, message: 'Please enter your Company Name',
}],
})(
<Input type="text" placeholder="Enter Your Company Name"/>
)}
</FormItem>
<FormItem label="Password" colon={false} style={{ marginBottom: "0px" }}>
{getFieldDecorator('password1', {
rules: [{
required: true, message: 'Please choose a password',
}, {
validator: this.validateToNextPassword,
}],
})(
<Input type="password" placeholder="Enter password"/>
)}
</FormItem>
<FormItem label="Confirm Password" colon={false} style={{ marginBottom: "0px" }}>
{getFieldDecorator('password2', {
rules: [{
required: true, message: 'Please confirm your password',
}, {
validator: this.compareToFirstPassword,
}],
})(
<Input type="password" onBlur={this.handleConfirmBlur} placeholder="Confirm password" />
)}
</FormItem>
{/* inserting into profiledata collection firestore */}
{/* ending profiledata collection insert firestore */}
{/*
<FormItem label="First Name" colon={false} >
<Input placeholder = "Enter Your First Name" style={{ maxWidth: '100%', maxHeight: '50%' , }}
value = { firstname }
onChange = {
(e) => setFirstName(e.target.value)
}/>
</FormItem>
<FormItem label="Last Name" colon={false}>
<Input placeholder = "Enter Your Last Name" style={{ maxWidth: '100%', maxHeight: '50%' }}
value = { lastname }
onChange = {
(e) => setLastName(e.target.value)
}/>
</FormItem>
<FormItem label="Company Name" colon={false}> <
Input placeholder = "Enter Your Company name." style={{ maxWidth: '100%', maxHeight: '50%' }}
value = { companyname }
onChange = {
(e) => setCompanyName(e.target.value)
}
/ >
</FormItem> */}
<FormItem>
<Button type="primary" htmlType="submit" id="submitButton">Register</Button>
</FormItem>
</Form>
);
}
}
const WrappedRegistrationForm = Form.create()(RegistrationForm);
export default withRouter(WrappedRegistrationForm)
am I missing something to actually render the elements on page?! All my other pages are rendering fine, just not this one! Any suggestions?
During user-registration, my backend informs the client if an email and/or username are currently in use by someone else. Below is the response logged into a webconsole thanks to Axios catch error.
I would like to map each email and username to the appropriate field.
My form is based off of Material-UI and react-hook-form
Here is the example of the error response provided by my Axios Instance.
{
"email":["This email is already in use"],
"username":["This username is already in use"]
}
Here is my complete react form, I cut some things out to make it easier to read:
export default function SignUp()
{
const { register, control, errors: fieldsErrors, handleSubmit } = useForm()
const onSubmit = (data, e) => {
console.log(data);
axiosInstance
.post(`api/user/register/`, {
email: data.email,
username: data.username,
password: data.password,
})
.then((res) => {
history.push('/login');
console.log(res);
console.log(res.data);
})
.catch(err => {
console.error(err.response.data);
}
)
};
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}></Avatar>
<Typography component="h1" variant="h5">
Sign up
</Typography>
<form className={classes.form} noValidate onSubmit={handleSubmit(onSubmit)}>
<FormControl fullWidth variant="outlined">
<Grid container spacing={2}>
<Grid item xs={12}>
<Controller
name="email"
as={
<TextField
variant="outlined"
required
fullWidth
id="email"
label="Email Address"
name="email"
autoComplete="email"
error={Boolean(fieldsErrors.email)}
onChange={
(evt) =>
{
let key = evt.currentTarget.name;
let value = evt.currentTarget.value;
handleChange({ [key]: value });
}
}
/>
}
control={control}
defaultValue=""
rules={{
required: 'Required',
pattern: {
value: /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i,
message: 'invalid email address'
}
}}
/>
</Grid>
<Grid item xs={12}>
<Controller
name="username"
as={
<TextField
variant="outlined"
required
fullWidth
id="username"
label="Username"
name="username"
onChange={
(evt) => {
let key = evt.currentTarget.name;
let value = evt.currentTarget.value;
handleChange({[key]: value});
}
}
/>
}
control={control}
defaultValue=""
rules={{
required: 'Required',
pattern: {
value: /^(?!.*\.\.)(?!.*\.$)[^\W][\w.]{0,29}$/i,
message: 'Invalid use of characters'
}
}}
/>
{fieldsErrors.username?.type && <p>{fieldsErrors.username?.message}</p>}
</Grid>
<Grid item xs={12}>
<Controller
name="password"
as={
<TextField
variant="outlined"
required
fullWidth
name="password"
label="Password"
type="password"
id="password"
autoComplete="current-password"
onChange={
(evt) =>
{
let key = evt.currentTarget.name;
let value = evt.currentTarget.value;
handleChange({ [key]: value });
}
}
/>
}
control={control}
defaultValue=""
/>
</Grid>
</Grid>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
onClick={handleSubmit}
>
Sign Up
</Button>
</FormControl>
</form>
</div>
</Container>
);
}
I've managed to create the error into a webalert, but that did not look nice. Is there any simple way to implement my catch error into my form?
I use this field error :
{fieldsErrors.username?.type && <p>{fieldsErrors.username?.message}</p>}
As my Regex error to warn users of illegal characters in their username. I was thinking maybe I could add the errors there? But I do not know how to do that.
You can leverage the useState hook to store errors from the API. Then you can use your template to render those errors in the same format that you display your regex errors.
On a side note, for security's sake it's not a great idea to tell a user that an email is already taken. But if your application doesn't have sensitive data then it's not a huge deal.
EDIT: example
export default function SignUp()
{
const [ apiError, setApiError ] = useState(null)
const onSubmit = (data, e) => {
setApiError(null)
axiosInstance
//...
.catch(err => {
setApiError(err.response.data.message)
console.error(err.response.data);
}
)
};
return (
//....
{apiError && <p>{apiError}</p>}
//....
)
I have a login page. If login is successful and token is present in local storage, I want to redirect to a private page /panel. I am calling all functions on the onSubmit() of my form.
Here's what my code looks like:
function LoginPage (){
const [state, setState] = useState({
email: '',
password: '',
});
const [submitted, setSubmitted] = useState(false);
function ShowError(){
if (!localStorage.getItem('token'))
{
console.log('Login Not Successful');
}
}
function FormSubmitted(){
setSubmitted(true);
console.log('Form submitted');
}
function RedirectionToPanel(){
console.log('ha');
if(submitted && localStorage.getItem('token')){
console.log('FInall');
return <Redirect to='/panel'/>
}
}
function submitForm(LoginMutation: any) {
const { email, password } = state;
if(email && password){
LoginMutation({
variables: {
email: email,
password: password,
},
}).then(({ data }: any) => {
localStorage.setItem('token', data.loginEmail.accessToken);
})
.catch(console.log)
}
}
return (
<Mutation mutation={LoginMutation}>
{submitted && <Redirect to='/panel'/>}
{(LoginMutation: any) => (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center'
}}>
<Avatar>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<Formik
initialValues={{ email: '', password: '' }}
onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
validationSchema={schema}
>
{props => {
const {
values: { email, password },
errors,
touched,
handleChange,
isValid,
setFieldTouched
} = props;
const change = (name: string, e: any) => {
e.persist();
handleChange(e);
setFieldTouched(name, true, false);
setState( prevState => ({ ...prevState, [name]: e.target.value }));
};
return (
<form style={{ width: '100%' }}
onSubmit={e => {e.preventDefault();
submitForm(LoginMutation);FormSubmitted();RedirectionToPanel()}}>
<TextField
variant="outlined"
margin="normal"
id="email"
fullWidth
name="email"
helperText={touched.email ? errors.email : ""}
error={touched.email && Boolean(errors.email)}
label="Email"
value={email}
onChange={change.bind(null, "email")}
/>
<TextField
variant="outlined"
margin="normal"
fullWidth
id="password"
name="password"
helperText={touched.password ? errors.password : ""}
error={touched.password && Boolean(errors.password)}
label="Password"
type="password"
value={password}
onChange={change.bind(null, "password")}
/>
{submitted && ShowError()}
<FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Remember me"
/>
<br />
<Button className='button-center'
type="submit"
disabled={!isValid || !email || !password}
// onClick={handleOpen}
style={{
background: '#6c74cc',
borderRadius: 3,
border: 0,
color: 'white',
height: 48,
padding: '0 30px'
}}
>
Submit</Button>
</form>
)
}}
</Formik>
</div>
</Container>
)
}
</Mutation>
);
}
export default LoginPage;
When I hit the submit button, I check the console for what happens inside the RedirectionToPanel() function. The first time, 'Ha' is printed but when I click on it for the second time, both 'Ha' & 'Finally' are printed. However, the redirection still doesn't happen.
If I use {submitted && <Redirect to='/panel'/>}after Mutation, I get this error on mutation:
This JSX tag's 'children' prop expects a single child of type '(mutateFunction: MutationFunction<any, Record<string, any>>, result: MutationResult<any>) => Element | null', but multiple children were provided.ts(2746)
If I use it after the return and before mutation, I get syntax errors on && and }.
As #wxker stated, you need to return <Redirect> elements in the render method in order for the redirect to actually occur. E.g. https://codesandbox.io/s/proud-rgb-d0g8e.
This is happening because submitForm and FormSubmitted both contain asynchronous operations. This means that calling them one after another followed by RedirectionToPanel does not guarantee that they execute in that order. Consider using the useEffect hook to do the redirect once the token is set.
Also, the reason why the page is not redirecting is because you are not returning the Redirect component into the DOM tree. You can fix this by inserting it with a conditional statement that checks the submitted state
//when localStorage.getItem('token') changes, execute the callback
useEffect(() => {
setSubmitted(true);
}, [localStorage.getItem('token')])
...
return (
...
<form style={{ width: '100%' }}
onSubmit={e => {e.preventDefault();
submitForm(LoginMutation);}}>
...
//This line can be anywhere in the return. Even at the end is fine
{submitted && <Redirect to='/panel'/>}
<Container />
);
If you want to do this without useEffect, you can use setSubmitted within submitForm. But even then, you must have {submitted && <Redirect to='/panel'/>} somewhere in your DOM
function submitForm(LoginMutation: any) {
const { email, password } = state;
if(email && password){
LoginMutation({
variables: {
email: email,
password: password,
},
}).then(({ data }: any) => {
localStorage.setItem('token', data.loginEmail.accessToken);
setSubmitted(true);
})
.catch(console.log)
}
}
return (
...
<form style={{ width: '100%' }}
onSubmit={e => {e.preventDefault();
submitForm(LoginMutation);}}>
...
//This line can be anywhere in the return. Even at the end is fine
{submitted && <Redirect to='/panel'/>}
<Container />
);
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 };