Logging in in the second time - reactjs

I am building a log in, just practice, so the problem is when I click Log in with the correct info, the first time it dont do nothing, it logs in the second time only. I think the problem is that the data stores in the state for first and then It verifies and continue to the next page
.
When the info is wrong it gives the error message for the first time but when I log in it logs in only with the second try
import { Link } from 'react-router-dom';
class Index extends Component {
state = {
errorMessage: false,
isValueCorrect: false,
info: {
email: '',
password: ''
},
login: {
email: 'Email#gmail.com',
password: '1234'
}
};
updateInfo = e => {
this.setState({
info: { ...this.state.login, [e.target.name]: e.target.value }
});
};
submit = e => {
e.preventDefault();
if (
this.state.info.email === this.state.login.email &&
this.state.info.password === this.state.login.password
) {
this.setState({ isValueCorrect: true });
} else {
this.setState({ errorMessage: true });
}
};
render() {
return (
<div className="text-center container mt-4" style={{ width: '50%' }}>
<form className="px-4 py-3" onSubmit={this.submit}>
<div className="form-group">
{this.state.errorMessage === true ? (
<p className="text-danger">The email or the password is wrong</p>
) : null}
<label>Email: </label>
<input
type="text"
placeholder="Email#example.com"
className="form-control"
name="email"
value={this.state.info.email}
onChange={this.updateInfo}
/>
</div>
<div className="form-group">
<label>Password: </label>
<input
type="text"
placeholder="Password"
className="form-control"
name="password"
value={this.state.info.password}
onChange={this.updateInfo}
/>
</div>
{this.state.isValueCorrect === true ? (
<Link to="Profile">
<button type="submit" className="btn btn-secondary mt-3">
Log in
</button>
</Link>
) : (
<button type="submit" className="btn btn-secondary mt-3">
Sign in
</button>
)}
<div>
<Link to="/register" className="badge badge-light p-2 m-2">
Register
</Link>
</div>
</form>
</div>
);
}
}
export default Index;

You're indeed correct in your assessment that the first click only validates the data. You're displaying either a submit button or a link pretending to be a submit button based on the isValueCorrect state flag. That flag is only set once the form is submitted whereas it should be set when the values are correct. There are a few ways you could fix this. Choose one, not both.
1. Set the flag when the values are correct.
updateInfo = e => {
const info = { ...this.state.info, [e.target.name]: e.target.value };
this.setState({
info,
isValueCorrect: (
info.email === this.state.login.email &&
info.password === this.state.login.password
)
});
};
And only handle error in submit.
submit = e => {
e.preventDefault();
if (!this.state.isValueCorrect) {
this.setState({ errorMessage: true });
}
};
2. Use Redirect instead of Link
import { Redirect } from 'react-router-dom';
Replace the conditional {this.state.isValueCorrect === true ... ternary with
<button type="submit" className="btn btn-secondary mt-3">
Sign in
</button>
and add a conditional path to render.
render() {
if (isValuesCorrect) return <Redirect to="Profile" />
return (
...
);
}
Other mistakes
login in state is never changed, so it should be a static constant of the class instead of a filed in state.
updateInfo spreads state.login into state.info, when it should propably spread state.info instead.

Related

How to fix "cannot update during an existing state transition (such as within `render`)" problem?

I get this problem even though my code doesn't update the state directly.
Here's my code:
export class Login extends React.Component {
static contextType = getContext();
_usernameValue = "";
_passwordValue = "";
// adding value to notification variable activates notification
state = { mode: "signin", notification: "" };
constructor(props) {
super(props);
this.showNotificationDeb = debounceOnce(this.showNotification, 200);
this.closeNotificationDeb = debounceOnce(this.closeNotification, 200);
}
signinClicked = () => {
this.context.dispatch({
type: "LOGIN",
payload: {
username: this._usernameValue,
password: this._passwordValue,
callback: this.incorrectCredentials,
},
});
};
forgotPasswordClicked = () => {
this.setState({ mode: "password" });
};
sendPasswordRequest = () => {
this.context.dispatch({
type: "FORGOT_PASSWORD",
payload: {
username: this._usernameValue,
password: this._passwordValue,
callback: this.passwordRequestSent,
},
});
};
incorrectCredentials = (errorMessage) => {
this.showNotificationDeb(errorMessage);
};
passwordRequestSent = (message) => {
this.showNotificationDeb(message);
};
restoreSigninWindow = () => {
this.setState({ mode: "signin" });
};
showNotification = (message) => {
console.log("aa");
this.setState({ notification: message });
};
closeNotification = () => {
if (this.state.notification) this.setState({ notification: "" });
};
render() {
return (
<div className={styles.container}>
<div className={styles.loginContainer}>
<Icon
rotate={90}
path={mdiCog}
size={2}
color="black"
className={styles.loginIcon}
/>
<p className={styles.loginTitle}>Sign in to RAS</p>
<div
className={`${styles.notificationContainer} ${
this.state.notification ? "" : styles.hideNotification
}`}
>
<p className={styles.notificationMessage}>
{this.state.notification}
</p>
<p
className={styles.notificationCloseButton}
onClick={() => this.closeNotification()}
>
x
</p>
</div>
<div className={styles.loginWindow}>
{this.state.mode === "signin" ? (
<React.Fragment>
<label className={styles.inputLabel}>Username</label>
<input
id="usernameInput"
className={styles.input}
onChange={(event) => {
this._usernameValue = event.target.value;
}}
></input>
<div className={styles.passwordLabelContainer}>
<label className={styles.inputLabel}>Password</label>
<p
className={styles.forgotPasswordLabel}
onClick={() => this.forgotPasswordClicked()}
>
Forgot password?
</p>
</div>
<input
id="passwordInput"
type="password"
className={styles.input}
onChange={(event) => {
this._passwordValue = event.target.value;
}}
></input>
<Button
variant="contained"
className={styles.button}
onClick={() => this.signinClicked()}
>
Sign in
</Button>
</React.Fragment>
) : (
<React.Fragment>
<div className={styles.backButtonContainer}>
<div onClick={() => this.restoreSigninWindow()}>
<Icon
path={mdiKeyboardBackspace}
size={0.85}
color="black"
className={styles.backIcon}
/>
<p>Back</p>
</div>
</div>
<label className={`${styles.inputLabel}`}>
Enter your email address. Password reset link will be send to
your email address.
</label>
<input
id="usernameInput"
className={styles.input}
placeholder="Enter your email address"
></input>
<Button
variant="contained"
className={styles.button}
onClick={() => this.sendPasswordRequest()}
>
Send
</Button>
</React.Fragment>
)}
</div>
</div>
</div>
);
}
}
As you see, I don't change the state directly. I do it always by calling the functions above. However, according to the React I am changing the state from inside render() function.
I do use lambda functions but I still see the problem.

how to use multiple ref in my react input

hello guys pls i have a challenge in my react app, i am trying to enable multiple choice login where a user can either use his mail or even hi username to login. i got it working from my server using postman but when i try it on my frontend it doesnt allow me to use multiple refs on it, i am only allowed to use either the username or email.
Here is my code
function Login() {
const password = useRef();
const email = useRef();
const username = useRef();
const { isFetching, dispatch } = useContext(AuthContext);
const handleClick = (e) => {
e.preventDefault();
try {
loginCall(
{
username: username.current.value,
email: email.current.value,
password: password.current.value,
},
dispatch
);
} catch (err) {
console.log(err.response);
}
};
return (
<>
<div className="log__bg">
<div className="login">
<div className="loginWrapper">
<div className="loginLeft">
<span className="loginDesc">Welcome Back</span>
<ToastContainer />
</div>
<div className="loginRight">
<form className="loginBox" onSubmit={handleClick} >
<input
placeholder="Email, phone, or username"
type="text"
required
className="loginInput"
ref={username}
/>
<input
placeholder="Password"
type="password"
required
minLength="6"
className="loginInput"
ref={password}
/>
<button
className="loginButton"
type="submit"
disabled={isFetching}
>
{isFetching ? (
<CircularProgress color="white" size="20px" />
) : (
"Log In"
)}
</button>
<Link id="auth_route" to="/register">
<button className="loginRegButton">
{isFetching ? (
<CircularProgress color="white" size="20px" />
) : (
"Create a New Account"
)}
</button>
</Link>
<div className="forgot__password">
<Link className="forgot__password__btn" to="/forgot-password">
Forgot Password?
</Link>
</div>
</form>
</div>
</div>
</div>
</div>
</>
);
}
export default Login;
Why you need 2 refs ? user will provide username or email in the same input field. Use just one ref than you can have a logic or regex in your backend to detect if the provided value is an email or a username:
const handleClick = (e) => {
e.preventDefault();
let username = null
let email = null
const emailRegex = /^\S+#\S+\.\S+$/
const inputValue = username.current.value
if(inputValue.match(emailRegex)){
email = inputValue
} else {
username = inputValue
}
try {
loginCall(
{
username: username,
email: email,
password: password.current.value,
},
dispatch
);
} catch (err) {
console.log(err.response);
}
};
With this if the user provided an email, username will be sent as null to the server and vice versa. Then you can check on the server if you get an email or a username.
UPDATE:
You can also just use a state and don't use ref at all:
const [usernameValue, setUsernameValue]=React.useState("")
const [passwordValue, setPasswordValue]=React.useState("")
const handleClick = (e) => {
e.preventDefault();
let username = null
let email = null
const emailRegex = /^\S+#\S+\.\S+$/
if(usernameValue.match(emailRegex)){
email = inputValue
} else {
username = inputValue
}
try {
loginCall(
{
username: username,
email: email,
password: passwordValue,
},
dispatch
);
} catch (err) {
console.log(err.response);
}
};
return (
...
<form className="loginBox" onSubmit={handleClick} >
<input
placeholder="Email, phone, or username"
type="text"
required
className="loginInput"
value={usernameValue}
onChange={e=>setUsernameValue(e.target.value)}
/>
<input
placeholder="Password"
type="password"
required
minLength="6"
className="loginInput"
value={passwordValue}
onChange={e=>setPasswordValue(e.target.value)}
/>
...
...
)
I agree with #PatersonCode, the best way would be to use React.useState and just one variable to store your input value.
You didn't mention what error you're getting, but here's an example that I hope might help you in some way:
https://codesandbox.io/s/choose-login-method-example-xgpfue?file=/src/Login.js
thanks to everyone that helped, i have solved it and the problem was that i needed to use one ref (email or username) and also that same ref in my loginCall. here is the updated code.
function Login() {
const password = useRef();
const username = useRef();
const { isFetching, dispatch } = useContext(AuthContext);
const handleClick = (e) => {
e.preventDefault();
try {
loginCall(
{
username: username.current.value,
email: username.current.value,
password: password.current.value,
},
dispatch
);
} catch (err) {
console.log(err.response);
}
};
return (
<>
<div className="log__bg">
<div className="login">
<div className="loginWrapper">
<div className="loginLeft">
<span className="loginDesc">Welcome Back</span>
<ToastContainer />
</div>
<div className="loginRight">
<form className="loginBox" onSubmit={handleClick} >
<input
placeholder="Email, phone, or username"
type="text"
required
className="loginInput"
ref={username}
/>
<input
placeholder="Password"
type="password"
required
minLength="6"
className="loginInput"
ref={password}
/>
<button
className="loginButton"
type="submit"
disabled={isFetching}
>
{isFetching ? (
<CircularProgress color="white" size="20px" />
) : (
"Log In"
)}
</button>
<Link id="auth_route" to="/register">
<button className="loginRegButton">
{isFetching ? (
<CircularProgress color="white" size="20px" />
) : (
"Create a New Account"
)}
</button>
</Link>
<div className="forgot__password">
<Link className="forgot__password__btn" to="/forgot-password">
Forgot Password?
</Link>
</div>
</form>
</div>
</div>
</div>
</div>
</>
);
}
export default Login;

Form submission canceled because the form is not connected in react js. Where am I going wrong?

**This is a redeem Voucher form which I have created , based on the API response data. I have to make the states either true for success or false on failure, untill we get the api response, Loading snippet will be displayed. **
Onclick of the avail subscription button, I'm not able to display the loading snippet. In the console it shows "Form submission canceled because the form is not connected ".
import React, { Component } from 'react';
import styles from './RedeemVoucher.module.scss'
import close from '../../../Assets/Images/close_grey.svg'
import verified from '../../../Assets/Images/verified.svg'
class RedeemVoucher extends Component {
constructor(props){
super(props);
this.state = {
voucherNumber:'',
pin:'',
redeemVoucherForm:true,
isLoading:false,
error:false,
verified:false
}
}
handleUserInput =(e)=> {
const name = e.target.name;
const value = e.target.value;
this.setState({ [name]: value.replace(/[-/+/?/./`/~/,/{/}/[!/|/#/#/$/%/^/&/*/(/)/_/>/</:/;/'/"/=A-Za-z]/g,"")});
}
handleSubscription=()=>{
this.setState({
isLoading:true,
redeemVoucherForm:false,
error:false,
verified:false
})
}
render() {
return (
<>
{this.state.redeemVoucherForm ?
<form className={styles.redeem_form}>
<img src={close} className={styles.close} onClick={this.props.close_RedeemVoucher_Prompt}/>
<p className={styles.head}>Redeem Voucher</p>
<p className={styles.description}>Enter your NCPA Membership Voucher details to avail your free subscription plan</p>
<label className={styles.voucher_number}>Voucher Number <span className={styles.star}>*</span></label>
<input
type="text"
placeholder='Enter Voucher Number'
className={styles.voucher_number_box}
value={this.state.voucherNumber}
name='voucherNumber'
autoComplete="off"
onChange={(event) => this.handleUserInput(event)}
/>
<label className={styles.pin}>Pin <span className={styles.star}>*</span></label>
<input
type="text"
placeholder="Enter Pin"
className={styles.pin_box}
value={this.state.pin}
autoComplete="off"
name='pin'
onChange={(event) => this.handleUserInput(event)}
/>
<button className={styles.avail_subs} onClick={this.handleSubscription} disabled= {!this.state.voucherNumber || !this.state.pin} >Avail Subscription</button>
</form>
: this.state.isloading ?
<div className={styles.loading}>
<p className={styles.verifying_info}>Verifying Information</p>
<p className={styles.please_wait}>Please Wait</p>
{/* loader */}
</div>
: this.state.error ?
<div className={styles.error}>
{/* API error response */}
<button className={styles.exit}>exit</button>
</div>
: this.state.verified ?
<div className={styles.verified}>
<p className={styles.verifying_info}>Verifying Information</p>
<p className={styles.please_wait}>Please Wait</p>
<img src={verified} className={styles.verified_img} />
</div>
: null}
</> )}
}
export default RedeemVoucher
I suspect your code is submitting the form and some default form actions are occurring. This is because button elements are of type="submit" by default when no type attribute is provided, and the form element has no onSubmit handler to prevent the default form actions from occurring. When the default form action occurs, generally the page will reload, this is likely wiping out your state update.
Explicitly declare the button to be type="submit" and move the handleSubscription callback to the form element's onSubmit handler. In handleSubscription prevent the default submit action.
handleSubscription = (event) => {
event.preventDefault();
this.setState({
isLoading: true,
redeemVoucherForm: false,
error: false,
verified: false
});
};
...
<form
className={styles.redeem_form}
onSubmit={handleSubscription}
>
...
<button
className={styles.avail_subs}
disabled={!this.state.voucherNumber || !this.state.pin}
>
Avail Subscription
</button>
</form>

React authentication not able to redirect to the right page

So,
I am trying to redirect the user to /home if the authentication is successful and it is.
however the user is redirected to
/?username=myusername#gmail.com&password=password
on the console log, it prints
Navigated to http://localhost:3000/?username=myusername#gmail.com&password=password
I am not sure why it does that, and what part of the code is controlling that. I didn't start the project.
I looked everywhere and it does not make sens. any one can point it out to me please?
Thanks
imports....
class Login extends Component {
constructor() {
super();
this.state = {
username: '',
password: '',
errors: {}
};
//binding functions
this.onChange = this.onChange.bind(this)
this.onSubmit = this.onSubmit.bind(this)
}
//bind state var with input value
onChange(e){this.setState({[e.target.name]: e.target.value})}
onSubmit(e) {
const user = {
username: this.state.username,
password: this.state.password
}
async function authenticate() {
.....
.....
.....
return data;
}
if (user.username.length === 0 || user.password.length === 0) {
notify.show("Access failure with insufficient or empty credentials", "custom", 500, myColor)
console.log("Access failure with insufficient or empty credentials")
} else {
authenticate()
.then(response =>{
console.log(response)
if (response.data.data!==0) {
console.log("--------------------------------")
//set the sessionStorage login
//sessionStorage.setItem("email_logged_in",user.username);
e.preventDefault();
this.props.history.push({
//redirect to home page
pathname : '/home',
state :user.username
})
}else{
//show failed notification
notify.show("login failed ! ", "custom", 500000, myColor)
}
})
//handle errors
.catch(err => {
notify.show('Error Authenticating ', "custom", 500000, myColor)
})
}
}
//rendering the login component
render() {
return (
<div className="container shadow component rounded col-sm-10 col-md-6 p-5 my-5">
<Notifications />
<div className="row">
<div className="col-md-8 mx-auto">
{/* Login Form*/}
<form noValidate onSubmit={this.onSubmit}>
<h1 className="h3 mb-3 font-weight-normal h1 text-center">Please sign in</h1>
<div className="form-group">
<label htmlFor="username">username </label>
<input
autoComplete="off"
type="username"
className="form-control form-styling"
name="username"
placeholder="Enter username"
value={this.state.username}
onChange={this.onChange}
/>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input
autoComplete="off"
type="password"
className="form-control form-styling"
name="password"
placeholder="Password"
value={this.state.password}
onChange={this.onChange}
/>
</div>
<button
type="submit"
className="btn btn-lg btn-block btn-signin btn-animate col-md-6 col-sm-8 col-sm-2 offset-md-3 "
>
Sing in
</button>
</form>
{/*End Login Form*/}
</div>
</div>
</div>
)
}
}
export default Login
You aren't preventing the browser from handling the form submit.
You don't call e.preventDefault() until you've had a response from your authenticate call, at which point the browser will have already processed the form. The form, in this case having no action or method properties set, defaults to a GET request on the current URL passing the fields of the form, thus resulting in:
/?username=myusername#gmail.com&password=password
When handling form submissions asynchronously, you should cancel the default browser behaviour as early as possible to avoid any unexpected behaviour e.g.
onSubmit(e) {
e.preventDefault();
// do AJAX form post
}

How to control a Link on react-router

I am trying to build a login, I verify the input and the data, the problem is that I don't know how to disable the Link when the Input is wrong. I mean I don't want the login to continue when the username and the password is wrong.
Can I disable the Link? or I need another solution
I really can't think of another solution, hope you can help me.
import { Link } from 'react-router-dom';
class Index extends Component {
state = {
info: {
email: '',
password: ''
},
login: {
email: 'Email.#gmail.com',
password: '1234'
}
};
updateInfo = e => {
this.setState({
info: { ...this.state.login, [e.target.name]: e.target.value }
});
};
submit = e => {
e.preventDefault();
if (
this.state.info.email === this.state.login.email &&
this.state.info.password === this.state.login.password
) {
console.log('true');
} else {
console.log('false');
}
};
render() {
return (
<div className="text-center container mt-4" style={{ width: '50%' }}>
<form className="px-4 py-3" onSubmit={this.submit}>
<div className="form-group">
<label>Email: </label>
<input
type="text"
placeholder="Email#example.com"
className="form-control"
name="email"
value={this.state.info.email}
onChange={this.updateInfo}
/>
</div>
<div className="form-group">
<label>Password: </label>
<input
type="text"
placeholder="Password"
className="form-control"
name="password"
value={this.state.info.password}
onChange={this.updateInfo}
/>
</div>
<Link to="Profile">
<button type="submit" className="btn btn-secondary mt-3">
Sign in
</button>
</Link>
<div>
<Link to="/register" className="badge badge-light p-2 m-2">
Register
</Link>
</div>
</form>
</div>
);
}
}
export default Index;
add new field in state isValueCorrect:
class Index extends Component {
state = {
isValueCorrect: false,
info: {
email: '',
password: ''
},
login: {
email: 'Email.#gmail.com',
password: '1234'
}
};
then check the values of password and login in updateInfo and if login and password are correct call this.setState({isValueCorrect: true}) method and then use simple if statement in your component in curly brackets like this:
{ this.state.isValueCorrect === true ?
( <Link to="Profile">
<button type="submit" className="btn btn-secondary mt-3">
Sign in
</button>
</Link>)
:
(null)
}
<button type="submit" className="btn btn-secondary mt-3">
Sign in
</button>
all you need to remove the Link and keep the button
then after the sign in succeeded then you can use:-
this.props.history.push('/Profile');
or
import { Redirect } from 'react-router-dom'
<Redirect to='/target' />

Resources