只能更新已安装或安装的组件。这通常意味着您在已卸载的组件

时间:2018-03-01 12:27:27

标签: ajax reactjs spinner setstate

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.

0 个答案:

没有答案