Homepage.js
import React, { Component } from 'react';
import { Route, Redirect, withRouter } from 'react-router-dom';
import $ from 'jquery';
import { css } from 'glamor';
import { ToastContainer } from 'react-toastify';
import toast from '../toast';
import { BarLoader } from 'react-spinners';
// ---------------- Custom components
import Header from '../Header/Header';
import Footer from '../Footer/Footer';
import RelayAnimation from '../RelayAnimation/RelayAnimation';
import UserLoginForm from '../UserLoginForm/UserLoginForm';
import UserSignUpForm from '../UserSignUpForm/UserSignUpForm';
import PassResetReqForm from '../PassResetReqForm/PassResetReqForm';
import PassResetForm from '../PassResetForm/PassResetForm';
import './HomePage.css';
// --------- Message for Network Error
const Msg = () => (
<div>
Error please, try again later <br /> or reload the Page.
</div>
);
class HomePage extends Component {
constructor(props) {
super(props);
this.state = {
loading: false
};
this.toggleLoader = this.toggleLoader.bind(this);
this.notifySuccess = this.notifySuccess.bind(this);
this.notifyError = this.notifyError.bind(this);
}
notifySuccess(msg) {
toast.success(msg);
}
notifyError(msg) {
toast.error(msg);
}
// --------- Toast Notifications ---------------
// notify = (status) => {
// // --------- Server Issue Toaster
// if (status === 'Bad Gateway') {
// toast.error(<Msg />, {
// className: {
// color: '#fff',
// minHeight: '60px',
// borderRadius: '8px',
// boxShadow: '2px 2px 20px 2px rgba(0,0,0,0.3)'
// }
// });
// }
// }
toggleLoader() {
this.setState({
loading: !this.state.loading
});
}
isAuthenticated() {
const token = localStorage.getItem('authToken');
if (token) {
return true;
}
}
componentDidMount() {
const currentLocationPath = this.props.location.pathname;
const urlForEmailVerification = currentLocationPath.includes('/api/v1/verifyEmailUser/');
if (urlForEmailVerification) {
const { token } = this.props.match.params; // token value from url params passed by <Route/>
if (token) {
const url = `/api/v1/verifyEmailUser/${token}`;
// api call to make the user's account verified in db based on token in url
$.ajax({
url: url,
dataType: 'json',
type: 'GET',
success: function (res) {
console.log(res);
this.notifySuccess('emailVerified');
this.props.history.push('/');
}.bind(this),
error: function (xhr, status, err) {
console.error(url, status, err.toString());
}.bind(this)
});
}
}
}
render() {
const currentLocationPath = this.props.location.pathname;
const isAuthenticated = this.isAuthenticated();
const resetPasswordPathname = currentLocationPath.includes('/api/v1/resetPassword/');
if (!isAuthenticated) {
return (
<div className="App d-flex flex-column">
{/* Navbar with brand logo and language change dropdown and signup/login button */}
< Header />
{/* Main Section with RelayStream Animation graphic and forms */}
<div className="container py-4 py-md-0 pt-lg-4 d-flex flex-grow" >
<div className={'LoginScreen d-flex align-items-center align-items-lg-start ' +
((currentLocationPath === '/login' ||
currentLocationPath === '/signup' ||
currentLocationPath === '/forgot-password' ||
resetPasswordPathname) ? 'justify-content-around' : 'justify-content-center')}>
{/* RelayStream Animation graphic */}
<RelayAnimation />
{/* forms to switch between based on path change by <Router/> */}
<Route path="/login" component={(props) => <UserLoginForm {...props} notifySuccess={this.notifySuccess} notifyError={this.notifyError} toggleLoader={this.toggleLoader} />} />
<Route path="/signup" component={(props) => <UserSignUpForm {...props} notifySuccess={this.notifySuccess} notifyError={this.notifyError} toggleLoader={this.toggleLoader} />} />
<Route path="/forgot-password" component={(props) => <PassResetReqForm {...props} notifySuccess={this.notifySuccess} notifyError={this.notifyError} toggleLoader={this.toggleLoader} />} />
<Route path="/api/v1/resetPassword/:token" component={(props) => <PassResetForm {...props} notifySuccess={this.notifySuccess} notifyError={this.notifyError} toggleLoader={this.toggleLoader} />} />
</div>
</div >
{/* Footer with copyright message */}
<Footer />
<div className={this.state.loading ? 'loader flex-column' : 'd-none'}>
<span className="loader__title">Loading...</span>
<BarLoader color={'#36D7B7'} loading={this.state.loading} />
</div>
{/* React toastify for toast notification */}
<ToastContainer className={{ textAlign: 'center' }} progressClassName={css({ background: '#007aff' })} />
</div >
);
} else {
return <Redirect to={'/dashboard'} />;
}
}
}
export default withRouter(HomePage);
UserLoginForm.js
import React, { Component } from 'react';
import { Link, Redirect } from 'react-router-dom';
import $ from 'jquery';
import { Animated } from 'react-animated-css';
import SocialButton from '../SocialButton/SocialButton';
// ---------------- Form components
import Form from 'react-validation/build/form';
import Button from 'react-validation/build/button';
// ---------------- Custom Form components & validations
import { Email, Password, required, noSpace, minChar8, email } from '../formValidation';
import FontAwesomeIcon from '@fortawesome/react-fontawesome';
import facebook from '@fortawesome/fontawesome-free-brands/faFacebookF';
import google from '@fortawesome/fontawesome-free-brands/faGooglePlusG';
import './UserLoginForm.css';
class UserLoginForm extends Component {
constructor(props) {
super(props);
this.state = {
fireRedirect: false
};
this.handleInputChange = this.handleInputChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSocialLogin = (user) => {
const provider = user._provider;
const name = user._profile.name;
const email = user._profile.email;
const profilePic = user._profile.profilePicURL;
const token = user._token.accessToken;
const data = { provider, name, email, profilePic, token };
console.log(data);
const url = '/api/v1/loginWithFacekbook'; // social login's api url
// api call for social logins
$.ajax({
url: url,
dataType: 'json',
type: 'POST',
data: data,
success: function (res) {
console.log('success response after api call ===>>', res);
const generatingAuthToken = res.object.generatingAuthToken;
const apikey = generatingAuthToken.apiKey;
const authToken = generatingAuthToken.authToken;
localStorage.setItem('apiKey', apikey);
localStorage.setItem('authToken', authToken);
// if social login was successful then redirect user to dashboard
this.setState({ fireRedirect: true });
}.bind(this),
error: function (xhr, status, err) {
console.log(status);
// if there was network issue notify user to try again later or refresh page
this.props.notifyError(err.toString());
console.error(url, status, err.toString());
}.bind(this)
});
}
handleSocialLoginFailure = (err) => {
console.error(err)
}
handleInputChange(event) {
const target = event.target;
const value = target.value;
const name = target.name;
// input field animation code - adds class to current focused input field's parent
if (value) {
target.parentElement.classList.add('input--filled');
} else {
target.parentElement.classList.remove('input--filled');
}
this.setState({
[name]: value
});
}
handleSubmit(event) {
event.preventDefault();
// show loading spinner
this.props.toggleLoader();
// get data from all field in form
const data = this.form.getValues();
const url = '/api/v1/loginUser'; // user login api url
// api call to generate token and apikey and login user to dashboard
$.ajax({
url: url,
dataType: 'json',
type: 'POST',
data: data,
success: function (res) {
console.log('success response after api call ===>>', res);
const obj = res.object;
const loginStatus = res.status;
const msg = res.message;
// check if authToken and apiKey was received
if (obj) {
// if data is found in database check if credentials provided were correct
if (loginStatus) {
JSON.stringify(obj);
// save apiKey and token in loacalStorage
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
let val = obj[key];
localStorage.setItem(key, val);
}
}
// turn off loader spinner
this.props.toggleLoader();
this.setState({ fireRedirect: true });
// if credentials were correct accept login then redirect user to dashboard
} else { // if credentials were wrong
// turn off loader spinner
// this.props.toggleLoader();
// then notify about wrong credentials
this.props.notifyError(msg);
}
} else { // if data was not found in database notify user to signup first
this.props.notifyError(msg);
// turn off loader spinner
// this.props.toggleLoader();
}
}.bind(this),
error: function (xhr, status, err) {
console.log(status);
// if there was network issue notify user to try again later or refresh page
this.props.notify(err.toString());
console.error(url, status, err.toString());
}.bind(this)
});
}
render() {
const { fireRedirect } = this.state;
return (
<Animated className="form-animation" animationIn="fadeInRight" animationOut="fadeOutLeft" isVisible={true}>
<Form className="userLoginForm" ref={c => { this.form = c }} onSubmit={this.handleSubmit} noValidate>
<h2 className="formTitle text-center">LOG IN</h2>
<Email
required
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,3}$"
id="email"
name="email"
type="email"
className="input__field input__field--madoka"
onChange={this.handleInputChange}
validations={[required, email]} />
{/* must wrap Password Field Component with "div.form-group" */}
<div className="form-group">
<Password
required
id="password"
name="password"
type="password"
minLength="8"
className="input__field input__field--madoka"
onChange={this.handleInputChange}
validations={[noSpace, required, minChar8]} />
{/* Optional Link Component below...
Note: If there is no requirement of Link or any Other Element just below <Password/> input field
in entire project then the wrapping "div.form-group" can also be put inside
Password Component's Template Located in formValidation.js file*/}
<Link to="/forgot-password" className="float-right forgotPassword">
<small>Forgot password ?</small>
</Link>
</div>
<div className="form-group submitGroup text-center">
<Button type="submit" className="btn btn--submit btn-rounded btn-outline-primary mx-auto">LOG IN</Button>
</div>
{/* login buttons for facebook and google login */}
<div className="socialLogin mx-auto">
{/* <a href="#" className="socialBtn socialBtn--facebook rounded-circle">
<FontAwesomeIcon icon={facebook} />
</a> */}
<SocialButton
className="socialBtn socialBtn--facebook rounded-circle"
provider='facebook' appId='873380466175223'
onLoginSuccess={this.handleSocialLogin}
onLoginFailure={this.handleSocialLoginFailure}
redirect="/dashboard">
<FontAwesomeIcon icon={facebook} />
</SocialButton>
<span className="seperator" />
<SocialButton
className="socialBtn socialBtn--googlePlus rounded-circle"
provider="google"
appId="843586925977-d7j31p5j0me5kqvcp29nr9s37reg5b5u.apps.googleusercontent.com"
onLoginSuccess={this.handleSocialLogin}
onLoginFailure={this.handleSocialLoginFailure}>
<FontAwesomeIcon icon={google} />
</SocialButton>
{/* <a href="#" className="socialBtn socialBtn--googlePlus rounded-circle">
<FontAwesomeIcon icon={google} />
</a> */}
</div>
{/* code to redirect user to dashboard page after successful login */}
{fireRedirect && (<Redirect to={'/dashboard'} />)}
</Form>
</Animated>
);
}
}
export default UserLoginForm;
UserSignUpForm.js
import React, { Component } from 'react';
import { Redirect } from 'react-router-dom';
import $ from 'jquery';
import { Animated } from 'react-animated-css';
// ---------------- Form components
import Form from 'react-validation/build/form';
import Button from 'react-validation/build/button';
// ---------------- Custom Form components & validations
import { UserName, Email, NewPassword, ConfirmPassword, noSpace, required, minChar8, email, confirmPassword } from '../formValidation';
import './UserSignUp.css';
class UserSignUpForm extends Component {
constructor(props) {
super(props);
this.state = {
userName: '',
email: '',
password: '',
confirmPassword: '',
fireRedirect: false
};
this.handleInputChange = this.handleInputChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.value;
const name = target.name;
// input field animation code - adds class to current focused input field's parent
if (value) {
target.parentElement.classList.add('input--filled');
} else {
target.parentElement.classList.remove('input--filled');
}
this.setState({
[name]: value
});
}
handleSubmit(event) {
event.preventDefault();
const data = this.form.getValues();
console.log(data);
// api call to sign up user
$.ajax({
url: '/api/v1/user',
dataType: 'json',
type: 'POST',
data: data,
success: function (res) {
console.log('success response after api call ===>>', res);
const signUpStatus = res.status;
const msg = res.message;
if (signUpStatus) {
// if signup was successful notify user
this.props.notifySuccess(msg);
// redirect user to login form
this.setState({ fireRedirect: true });
} else {
this.props.notifyError(msg); // notify user on signup fail
}
}.bind(this),
error: function (xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
}
render() {
const { fireRedirect } = this.state;
return (
<Animated className="form-animation" animationIn="fadeInRight" animationOut="fadeOutLeft" isVisible={true}>
<Form className="userSignUpForm" ref={c => { this.form = c }} onSubmit={this.handleSubmit}>
<h2 className="formTitle text-center">SIGN UP</h2>
<UserName
required
id="userName"
name="userName"
ref={c => { this.UserName = c }}
value={this.state.userName}
type="text"
className="input__field input__field--madoka"
validations={[required]}
onChange={this.handleInputChange} />
<Email
required
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,3}$"
id="email"
name="email"
ref={c => { this.Email = c }}
value={this.state.email}
type="email"
className="input__field input__field--madoka"
validations={[required, email]}
onChange={this.handleInputChange} />
<div className="form-group">
<NewPassword
required
id="password"
name="password"
ref={c => { this.NewPassword = c }}
value={this.state.password}
type="password"
minLength="8"
className="input__field input__field--madoka"
onChange={this.handleInputChange}
validations={[noSpace, required, minChar8]} />
</div>
<div className="form-group">
<ConfirmPassword
required
id="confirmPassword"
name="confirmPassword"
ref={c => { this.ConfirmPassword = c }}
value={this.state.confirmPassword}
type="password"
minLength="8"
className="input__field input__field--madoka"
onChange={this.handleInputChange}
validations={[noSpace, required, confirmPassword]} />
</div>
<div className="form-group submitGroup text-center">
<Button type="submit" className="btn btn--submit btn-rounded btn-outline-primary mx-auto">SIGN UP</Button>
</div>
{/* code to redirect user to dashboard page after successful login */}
{fireRedirect && (<Redirect to={'/login'} />)}
</Form>
</Animated>
);
}
}
export default UserSignUpForm;
我面临很多问题。首先我在homepage.js中有一个微调器,当this.state.loading = true时,它在整个页面上显示为叠加,最初在构造函数中设置为False。
问题1.)当我们提交登录表单时(如果注册已经完成),加载程序在屏幕上显示小部分秒,但是&#34; this.setState({fireRedirect:true}); &#34;这段代码应该将用户重定向到它所执行的仪表板,但在控制台中出错:
index.js:2178 Warning: Cannot update during an existing state transition (such as within `render` or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to `componentWillMount`.
index.js:2178 Warning: Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op.
Please check the code for the UserLoginForm component.
问题2.)如果我从UserSignUpForm创建一个新用户,它将我们重定向到UserLoginForm。现在,如果我尝试登录,我只得到这个错误:
index.js:2178 Warning: Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op.
Please check the code for the UserLoginForm component.
问题3.)如果我删除或评论&#34; this.toggleLoader();&#34;来自ajax之前的成功函数&#34; this.setState({fireRedirect:true}); &#34;这条线。然后加载程序显示在屏幕上,但即使页面没有重定向也没有消失,控制台会出现此错误:
index.js:2178 Warning: Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op.
Please check the code for the UserLoginForm component.