ReactJS和Redux:无法在已卸载的组件上执行React状态更新

时间:2018-12-14 20:33:31

标签: reactjs redux

以下是问题的.gif记录:http://g.recordit.co/1gy4gyT7jk.gif

我的Dashboard组件在登录后首次加载时,在控制台中返回以下错误。

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
    in PlaidLink (at Dashboard.js:51)
    in div (at Dashboard.js:44)index.js:1452

尽管出现此错误,但应用程序内的所有内容均正常运行,并且在登录后刷新页面后,错误消失了(有关错误的示例,请参阅.gif)。

这是我的 Login.js 文件。

import React, { Component } from "react";
import { Link } from "react-router-dom";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { loginUser } from "../../actions/authActions";
import classnames from "classnames";

class Login extends Component {
  constructor() {
    super();
    this.state = {
      email: "",
      password: "",
      errors: {}
    };
  }

  componentDidMount() {
    // If logged in and user navigates to Login page, should redirect them to dashboard
    if (this.props.auth.isAuthenticated) {
      this.props.history.push("/dashboard");
    }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.auth.isAuthenticated) {
      this.props.history.push("/dashboard");
    }

    if (nextProps.errors) {
      this.setState({
        errors: nextProps.errors
      });
    }
  }

  onChange = e => {
    this.setState({ [e.target.id]: e.target.value });
  };

  onSubmit = e => {
    e.preventDefault();

    const userData = {
      email: this.state.email,
      password: this.state.password
    };

    this.props.loginUser(userData);
  };

  render() {
    const { errors } = this.state;

    return (
      <div className="container">
        <div style={{ marginTop: "4rem" }} className="row">
          <div className="col s8 offset-s2">
            <Link to="/" className="btn-flat waves-effect">
              <i className="material-icons left">keyboard_backspace</i> Back to
              home
            </Link>
            <div className="col s12" style={{ paddingLeft: "11.250px" }}>
              <h4>
                <b>Login</b> below
              </h4>
              <p className="grey-text text-darken-1">
                Don't have an account? <Link to="/register">Register</Link>
              </p>
            </div>
            <form noValidate onSubmit={this.onSubmit}>
              <div className="input-field col s12">
                <input
                  onChange={this.onChange}
                  value={this.state.email}
                  error={errors.email}
                  id="email"
                  type="email"
                  className={classnames("", {
                    invalid: errors.email || errors.emailnotfound
                  })}
                />
                <label htmlFor="email">Email</label>
                <span className="red-text">
                  {errors.email}
                  {errors.emailnotfound}
                </span>
              </div>
              <div className="input-field col s12">
                <input
                  onChange={this.onChange}
                  value={this.state.password}
                  error={errors.password}
                  id="password"
                  type="password"
                  className={classnames("", {
                    invalid: errors.password || errors.passwordincorrect
                  })}
                />
                <label htmlFor="password">Password</label>
                <span className="red-text">
                  {errors.password}
                  {errors.passwordincorrect}
                </span>
              </div>
              <div className="col s12" style={{ paddingLeft: "11.250px" }}>
                <button
                  style={{
                    width: "150px",
                    borderRadius: "3px",
                    letterSpacing: "1.5px",
                    marginTop: "1rem"
                  }}
                  type="submit"
                  className="btn btn-large waves-effect waves-light hoverable blue accent-3"
                >
                  Login
                </button>
              </div>
            </form>
          </div>
        </div>
      </div>
    );
  }
}

Login.propTypes = {
  loginUser: PropTypes.func.isRequired,
  auth: PropTypes.object.isRequired,
  errors: PropTypes.object.isRequired
};

const mapStateToProps = state => ({
  auth: state.auth,
  errors: state.errors
});

export default connect(
  mapStateToProps,
  { loginUser }
)(Login);

这是我的 Dashboard.js 文件。

import React, { Component } from "react";
import PlaidLink from "react-plaid-link";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { logoutUser } from "../../actions/authActions";
import {
  getAccounts,
  setAccountsLoading,
  addAccount
} from "../../actions/accountActions";

import Transactions from "./Transactions";

class Dashboard extends Component {
  componentDidMount() {
    this.props.getAccounts();
  }

  // Logout
  onLogoutClick = e => {
    e.preventDefault();
    this.props.logoutUser();
  };

  // Add account
  handleOnSuccess = (token, metadata) => {
    const plaidData = {
      public_token: token,
      metadata: metadata
    };

    this.props.addAccount(plaidData);
  };

  render() {
    const { accounts, loading } = this.props.plaid;

    let dashboardContent;

    if (loading) {
      dashboardContent = <p>Loading...</p>;
    } else if (accounts === null || accounts.length === 0) {
      dashboardContent = (
        <div>
          <h4>
            <b>Welcome,</b> User
          </h4>
          <p className="flow-text grey-text text-darken-1">
            To get started, link your first bank account below
          </p>
          <PlaidLink
            clientName="Mosaic"
            className="btn btn-large waves-effect waves-light hoverable blue accent-3"
            env="sandbox"
            product={["auth", "transactions"]}
            publicKey="0c3ff69a2efea552189de8b7fbbc0f"
            onSuccess={this.handleOnSuccess}
            style={{
              width: "185px",
              letterSpacing: "1.5px",
              borderRadius: "3px",
              marginTop: "1rem"
            }}
          >
            Link Account
          </PlaidLink>
          <button
            style={{
              width: "185px",
              borderRadius: "3px",
              letterSpacing: "1.5px",
              marginTop: "1rem"
            }}
            onClick={this.onLogoutClick}
            className="btn btn-large waves-effect waves-light hoverable red accent-3"
          >
            Logout
          </button>
        </div>
      );
    } else {
      dashboardContent = <Transactions accounts={accounts} />;
    }

    return (
      <div className="container">
        <div className="row">
          <div className="col s12 center-align">{dashboardContent}</div>
        </div>
      </div>
    );
  }
}

Dashboard.propTypes = {
  logoutUser: PropTypes.func.isRequired,
  getAccounts: PropTypes.func.isRequired,
  addAccount: PropTypes.func.isRequired,
  auth: PropTypes.object.isRequired,
  plaid: PropTypes.object.isRequired
};

const mapStateToProps = state => ({
  auth: state.auth,
  plaid: state.plaid
});

export default connect(
  mapStateToProps,
  { logoutUser, getAccounts, setAccountsLoading, addAccount }
)(Dashboard);

这是由于我的组件引起的问题,还是与我的Redux动作和reducer的触发方式有关?

2 个答案:

答案 0 :(得分:4)

您可以尝试将this.setState = (state,callback)=>{ return; };写入componentWillUnmount

答案 1 :(得分:0)

选项1:使用 withRouter HOC

import { withRouter } from 'react-router-dom';
...
export default withRouter(Login)

选项2:使用重定向

<Redirect to='/dashboard' />