How to control a Link on react-router - reactjs

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' />

Related

Redirect doesn;t work, while {this.props.history} does

While an action is successful, redirect is not working but history.replace is working.
Why??
import React, { Component } from "react";
import { Formik, Form, Field, ErrorMessage } from "formik";
import * as Yup from "yup";
import { withRouter } from "react-router-dom";
class Login extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div id="loginContainer" className="signinup-container">
<h3 className="mb-4"> Log In </h3>
<Formik
initialValues={{
email: "",
password: "",
rememberMe: false,
error: ""
}}
validationSchema={Yup.object().shape({
email: Yup.string()
.required("Please enter email to login.")
.email("Please enter a valid email."),
password: Yup.string().required("Please enter your password.")
})}
onSubmit={(values, { resetForm, setErrors, setSubmitting }) => {
setTimeout(() => {
console.log("Logging in", values);
setSubmitting(false);
return <Redirect to="/dashboard" />;
//this.props.history.replace("/dashboard");
//this.props.history.push('/dashboard');
}, 500);
}}
>
{props => {
const {
values,
touched,
errors,
isSubmitting,
handleChange
} = props;
return (
<Form id="loginForm" className="signinupForm" noValidate>
<ErrorMessage
name="error"
component="span"
className="login-error"
/>
<div className="form-group ">
<label className="form-label" htmlFor="email">
Email
</label>
<Field
type={"email"}
name="email"
placeholder="Enter your email"
className={
"form-control" +
(errors.email && touched.email ? " is-invalid" : "")
}
/>
<ErrorMessage
name="email"
component="span"
className="invalid-input"
/>
</div>
{/* Email */}
<div className="form-group position-relative">
<label className="form-label" htmlFor="password">
Password
</label>
<Field
type={"password"}
name="password"
placeholder="Enter your password"
className={
"form-control" +
(errors.password && touched.password ? " is-invalid" : "")
}
/>
<ErrorMessage
name="password"
component="span"
className="invalid-input"
/>
</div>
{/* Password */}
<div className="form-group">
<label className="form-label" htmlFor="rememberMe">
<input
type="checkbox"
id="rememberMe"
name="rememberMe"
onChange={handleChange}
defaultChecked={values.rememberMe}
value={values.rememberMe}
/>
Remember me
</label>
</div>
{/* Rememeber Me */}
{isSubmitting ? (
<span className="loader-gif">loading</span>
) : null}
<button
type="submit"
className="btn btn-filled"
disabled={isSubmitting}
>
Login
</button>
{/*Submit */}
</Form>
);
}}
</Formik>
</div>
);
}
}
export default withRouter(Login);
Please go to login page and check this.
Codesandbox link - https://codesandbox.io/s/winter-hooks-s9vgx
You are calling your Redirect JSX component from onSubmit method. However you cannot do that since you need to return the JSX elements from within the render method which is why you need to use history to update route
onSubmit={(values, { resetForm, setErrors, setSubmitting }) => {
setTimeout(() => {
console.log("Logging in", values);
setSubmitting(false);
this.props.history.replace("/dashboard");
}, 500);
You must be using slash:
to='/dashboard'
As what #Shubham Khatri said, but if you want to use <Redirect> you can create a state and detect if logged and then redirect it, like this.
Changes are adding
this.state = {
isLoggedIn: false
};
And in render
if (this.state.isLoggedIn) return <Redirect to="/dashboard" />;
in onSubmit
this.setState({ isLoggedIn: true });

store user data globally in react redux for login logout purpose

i am new to react and redux, i just have an idea of both using that i am creating an app where i need to store the userData globally so that it is accessible via the whole application. i return userData the from backend using lumen but how to pass it to whole application components like header and other components to manage authentication.
below i paste the code, could any one help me to finish this.
Login component alone i have worked, not in redux as i am not good at it
import React, { Component } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
import "./assets/vendor/fontawesome-free/css/all.min.css";
import "./assets/css/sb-admin-2.min.css";
import "bootstrap/dist/js/bootstrap.bundle.min";
import validator from "simple-react-validator";
import confData from "./../Config/Config";
const apiKey = confData.apiKey;
const apiURL = confData.apiURL;
export default class Login extends Component {
constructor(props) {
super(props);
this.state = {
isLoggedIn: false,
email: "",
password: "",
uData: [],
id: ""
};
this.validator = new validator(this);
}
componentDidMount = () => {
document.title = "Sample | Login";
};
loginSubmit(e) {
e.preventDefault();
if (this.validator.allValid()) {
let email = this.state.email;
let password = this.state.password;
var self = this;
axios
.post(apiURL + "auth/login", {
email: email,
password: password
})
.then(function(response) {
console.log(response.data);
self.isLoggedIn = true;
self.uData = response.data;
self.setState({
email: self.uData.email,
id: self.uData.id,
isLoggedIn: true
});
localStorage.setItem("token", JSON.stringify(self.uData));
})
.catch(function(error) {
console.log(error.response);
this.isLoggedIn = false;
});
} else {
this.validator.showMessages();
// rerender to show messages for the first time
this.forceUpdate();
}
}
handleEmailChange(event) {
this.setState({
email: event.target.value
});
}
handlePasswordChange(event) {
this.setState({
password: event.target.value
});
}
render() {
return (
<div className="row justify-content-center">
<div className="col-xl-10 col-lg-12 col-md-9">
<div className="card o-hidden border-0 shadow-lg my-5">
<div className="card-body p-0">
<div className="row">
<div className="col-lg-6 d-none d-lg-block"></div>
<div className="col-lg-6">
<div className="p-5">
<div className="text-center">
<h1 className="h4 text-gray-900 mb-4">Welcome Back!</h1>
<Notifications options={{ zIndex: 200, top: "50px" }} />
</div>
<form
className="user"
onSubmit={this.loginSubmit.bind(this)}
>
<div className="form-group">
<input
type="email"
className="form-control form-control-user"
id="exampleInputEmail"
aria-describedby="emailHelp"
placeholder="Enter Email Address..."
onChange={this.handleEmailChange.bind(this)}
value={this.state.email}
/>
<span style={{ color: "#ff0000" }}>
{this.validator.message(
"Email",
this.state.email,
"required|email"
)}
</span>
</div>
<div className="form-group">
<input
type="password"
className="form-control form-control-user"
id="exampleInputPassword"
placeholder="Password"
onChange={this.handlePasswordChange.bind(this)}
value={this.state.password}
/>
<span style={{ color: "#ff0000" }}>
{this.validator.message(
"Password",
this.state.password,
"required"
)}
</span>
</div>
<button className="btn btn-primary btn-user btn-block">
Login
</button>
<hr />
<Link
to="#"
className="btn btn-google btn-user btn-block"
>
<i className="fab fa-google fa-fw"></i> Login with
Google
</Link>
<Link
to="#"
className="btn btn-facebook btn-user btn-block"
>
<i className="fab fa-facebook-f fa-fw"></i> Login with
Facebook
</Link>
</form>
<hr />
<div className="text-center">
<Link className="small" to="#">
Forgot Password?
</Link>
</div>
<div className="text-center">
<Link className="small" to="#">
Create an Account!
</Link>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
here i get the json return data from lumen, now i need to authenticate and maintain data all over the components using redux and autheenter code here and authenticate pages after user login.
Checkout redux docs first, and they have good example for newbies https://redux.js.org/basics/example
And user prettier, 2 space tabs (instead of 8), object destructurein https://dev.to/sarah_chima/object-destructuring-in-es6-3fm your code is pretty unreadable and NEVER use jquery with react.

Logging in in the second time

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.

Uncaught TypeError: Cannot read property 'value' of undefined React app

I am fairly new to learning react and I am using a react boilerplate code from here. I am having trouble getting it started; I have firebase set up and need some help getting past this error. I believe that I should be using the bind method somewhere but I'm not too sure.
Error:
register.jsx:19 Uncaught TypeError: Cannot read property 'value' of undefined
at UserRegister.onFormSubmit (register.jsx:19)
at Object.ReactErrorUtils.invokeGuardedCallback (ReactErrorUtils.js:71)
at executeDispatch (EventPluginUtils.js:79)
at Object.executeDispatchesInOrder (EventPluginUtils.js:102)
at executeDispatchesAndRelease (EventPluginHub.js:43)
at executeDispatchesAndReleaseTopLevel (EventPluginHub.js:54)
at Array.forEach ()
at forEachAccumulated (forEachAccumulated.js:23)
at Object.processEventQueue (EventPluginHub.js:259)
at runEventQueueInBatch (ReactEventEmitterMixin.js:18)
Code:
import React, { Component } from 'react';
import { browserHistory } from 'react-router';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { registerUser } from '../../actions/firebase_actions';
class UserRegister extends Component {
constructor(props) {
super(props);
this.onFormSubmit = this.onFormSubmit.bind(this);
this.state = {
message: '',
};
}
onFormSubmit(event) {
event.preventDefault();
const email = this.email.value;
const password = this.password.value;
this.registerUser({ email, password }).then((data) => {
if (data.payload.errorCode) {
this.setState({ message: data.payload.errorMessage })
;
} else {
browserHistory.push('/profile');
}
}
);
}
render() {
return (
<div className="col-md-4">
<form id="frmRegister" role="form" onSubmit={this.onFormSubmit}>
<p>{this.state.message}</p>
<h2>Register</h2>
<div className="form-group">
<label htmlFor="txtRegEmail">Email address</label>
<input
type="email" className="form-control" ref="email" id="txtEmail" placeholder="Enter email"
name="email"
/>
</div>
<div className="form-group">
<label htmlFor="txtRegPass">Password</label>
<input
type="password" className="form-control" ref="password" id="txtPass" placeholder="Password"
name="password"
/>
</div>
<button type="submit" className="btn btn-default">Register</button>
<br /> <br />
<a
href="#" className="btn btn-block btn-social btn-facebook" onClick={() => {
this.loginWithProvider('facebook');
}} data-provider="facebook"
>Facebook</a>
<a
href="#" className="btn btn-block btn-social btn-twitter" onClick={() => {
this.loginWithProvider('twitter');
}} data-provider="twitter"
>Twitter</a>
<a
href="#" className="btn btn-block btn-social btn-google" onClick={() => {
this.loginWithProvider('google');
}} data-provider="twitter"
>Google</a>
<a
href="#" className="btn btn-block btn-social btn-github" onClick={() => {
this.loginWithProvider('github');
}} data-provider="twitter"
>Github</a>
</form>
</div>
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({
registerUser,
}, dispatch);
}
function mapStateToProps(state) {
return { currentUser: state.currentUser };
}
export default connect(mapStateToProps, mapDispatchToProps)(UserRegister);
Try to use state for keep value of text input
import React, { Component } from 'react';
import { browserHistory } from 'react-router';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { registerUser } from '../../actions/firebase_actions';
class UserRegister extends Component {
constructor(props) {
super(props);
this.onFormSubmit = this.onFormSubmit.bind(this);
this.state = {
message: '',
email: '', // Add
password: '', // Add
};
}
onFormSubmit(event) {
event.preventDefault();
const email = this.state.email; // Change
const password = this.state.password; // Change
registerUser({ email, password }).then((data) => { // change
if (data.payload.errorCode) {
this.setState({ message: data.payload.errorMessage })
;
} else {
browserHistory.push('/profile');
}
}
);
}
render() {
return (
<div className="col-md-4">
<form id="frmRegister" role="form" onSubmit={this.onFormSubmit}>
<p>{this.state.message}</p>
<h2>Register</h2>
<div className="form-group">
<label htmlFor="txtRegEmail">Email address</label>
<input
type="email" className="form-control" ref="email" id="txtEmail" placeholder="Enter email"
name="email" value={this.state.email} onChange={e => this.setState({email: e.target.value})} // Change
/>
</div>
<div className="form-group">
<label htmlFor="txtRegPass">Password</label>
<input
type="password" className="form-control" ref="password" id="txtPass" placeholder="Password"
name="password" value={this.state.password} onChange={e => this.setState({password: e.target.value})} // Change
/>
</div>
<button type="submit" className="btn btn-default">Register</button>
<br /> <br />
<a
href="#" className="btn btn-block btn-social btn-facebook" onClick={() => {
this.loginWithProvider('facebook');
}} data-provider="facebook"
>Facebook</a>
<a
href="#" className="btn btn-block btn-social btn-twitter" onClick={() => {
this.loginWithProvider('twitter');
}} data-provider="twitter"
>Twitter</a>
<a
href="#" className="btn btn-block btn-social btn-google" onClick={() => {
this.loginWithProvider('google');
}} data-provider="twitter"
>Google</a>
<a
href="#" className="btn btn-block btn-social btn-github" onClick={() => {
this.loginWithProvider('github');
}} data-provider="twitter"
>Github</a>
</form>
</div>
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({
registerUser,
}, dispatch);
}
function mapStateToProps(state) {
return { currentUser: state.currentUser };
}
export default connect(mapStateToProps, mapDispatchToProps)(UserRegister);
This is bad practice. You should only inject values via: onChange={e => this.setState({email: e.target.value})} as mentioned already above.
I suggest you to start with a clean boilerplate (create-react-app is imo the best start) and setup first one state, for example the username/email. Log it into console before you even try to send it to firebase. This way, you always make sure that your app itself is working fine. Otherwise its always hard to tell where the issue is really coming from.
I wrote for you how you should approach this thing a small code snippet, which you can find here:
Click at the right top to open the app in a new window (symbol at the right top inside the browser of the editor) and open up your console. When you type in your email, it gets logged on submit. I suggest you to copy this and add all the values you want to log (password etc.) and first always log these into the console. If this works, add your firebase auth and try to add the push function for the database.
Greetings!

How to render two components so that both content shows on one page

I have a login and signup form using modals. They both work just fine, but when rendering them in my home page only the login form will pop up for both but if I switch the order signup will show up for both. I'm not sure if I am missing something or need to wrap them up differently.
This is my signup but my login is pretty much the same just with different naming.
import React from "react";
import SignUp from "../SignUp";
import Login from "../Login";
const Home = () => (
<div>
<Login />
<SignUp />
</div>
);
export default Home;
import React, { Component } from "react";
import axios from "axios";
import { Redirect } from "react-router-dom";
class SignUp extends Component {
constructor() {
super()
this.state = {
username: '',
password: '',
confirmPassword: '',
redirectTo: null
}
this.handleSubmit = this.handleSubmit.bind(this)
this.handleChange = this.handleChange.bind(this)
};
handleChange(event) {
this.setState({
[event.target.name]: event.target.value
});
};
handleSubmit(event) {
event.preventDefault()
// TODO - validate!
axios
.post('/auth/signup', {
username: this.state.username,
password: this.state.password
}).then(response => {
console.log(response)
if (!response.data.errmsg) {
console.log('youre good')
this.setState({
redirectTo: '/login'
});
} else {
console.log('duplicate')
}
});
};
render() {
if (this.state.redirectTo) {
return <Redirect to={{ pathname: this.state.redirectTo }} />
}
return (
<div>
<a data-toggle="modal" data-
target="#exampleModal">Signup</a>
<div className="modal fade" id="exampleModal" tabIndex="-1"
role="dialog" aria-labelledby="exampleModalLabel" aria-
hidden="true">
<div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title"
id="exampleModalLabel">Sign Up</h5>
<button type="button" className="close" data-
dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div className="container ">
<div className="modal-body">
<div className="SignupForm">
<label
htmlFor="username">Username</label><br />
<input type="text" name="username"
value={this.state.username} onChange=
{this.handleChange} /><br />
<label
htmlFor="password">Password</label><br />
<input type="password"
name="password" value={this.state.password}
onChange=
{this.handleChange} /><br />
<label htmlFor="confirmPassword">Confirm
Password</label>
<br />
<input type="password"
name="confirmPassword" value=
{this.state.confirmPassword} onChange=
{this.handleChange}/>
</div>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-secondary"
data-dismiss="modal">Close</button>
<button type="submit" onClick={this.handleSubmit}
className="btn btn-primary">Submit</button>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
};
export default SignUp;
Usage of Bootstrap JS Modals will interfere with the proper functioning React's Virtual DOM.
Kindly use a react-compatible modal, such as: Material-Ui Dialog
Your component looks alright. Taking by your word, if they render perfectly on separate pages. Few points i'll want you to look for
If you are directly rendering them as two directly by attaching both components straightaway
render() {<div> <SignUp /> <Login /> </div> }
If this causes you to open the same component twice, you might want to check the id's of these elements. It's quite probable that both of your components are defined by the same id and the one which renders first, gets called first.
Also, if you want to place some conditions on these components, you can assign them in states, and set them according to the conditions.

Resources