我收到错误消息警告:无法对未安装的组件执行 React 状态更新

I am getting the error message Warning: Can't perform a React state update on an unmounted component

我收到错误消息

警告:无法对未安装的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要修复,取消 componentWillUnmount method.in 登录中的所有订阅和异步任务(由 Context.Consumer` 创建)

我已针对该问题尝试了最常见的修复方法,但没有奏效。使用 _isMounted = false 的修复; componentDidMount() { this._isMounted = true;} if (this._isMounted) {this.setState({ data: data});}

我也在用Context。

SessionContex.js

import React, { Component, createContext } from "react";

export const SessionContext = createContext();

class SessionContextProvider extends Component {
  _isMounted = false;
  state = {
    is_logged_in: false,
    personid: " ",
    firstname: " ",
    lastname: " "
  };
  loggedIn = (loginvalue, personid, firstname, lastname) => {
    this.setState({
      is_logged_in: loginvalue,
      personid: personid,
      firstname: firstname,
      lastname: lastname
    });
  };

  loggedOut = () => {
    this.setState({
      is_logged_in: false,
      personid: " ",
      firstname: " ",
      lastname: " "
    });
  };
  componentDidMount = () => {
    this._isMounted = true;
    const login = localStorage.getItem("is_logged");
    const id = localStorage.getItem("personid");
    const firstname = localStorage.getItem("firtname");
    const lastname = localStorage.getItem("lastname");
    console.log(login);
    if (this._isMounted) {
      this.setState({
        is_logged_in: login,
        personid: id,
        firstname: firstname,
        lastname: lastname
      });
    }
  };

  componentWillUnmount() {
    this._isMounted = false;
  }
  render() {
    return (
      <SessionContext.Provider
        value={{
          ...this.state,
          loggedIn: this.loggedIn,
          loggedOut: this.loggedOut
        }}
      >
        {this.props.children}
      </SessionContext.Provider>
    );
  }
}

export default SessionContextProvider;

Login.jsx

import React, { Component } from "react";
import { SessionContext } from "../context/SessionContext";
import "../Stylesheets/Login.css";
import "..//Stylesheets/global.css";
import { Redirect } from "react-router-dom";

class Login extends Component {
  _isMounted = false;
  static contextType = SessionContext;

  state = {
    password: " ",
    email: " ",
    couldNotfindLogin: true,
    redirect: false
  };

  handleChange = e => {
    this.setState({
      [e.target.name]: e.target.value,
      [e.target.name]: e.target.value
    });
  };

  handleSubmit = async e => {
    e.preventDefault();
    const data = this.state;
    console.log(data);
    const response = await fetch("http://localhost:3080/users/login", {
      method: "POST",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json"
      },
      body: JSON.stringify(data)
    });

    const reply = await response;

    if (reply.status === 200) {
      const userData = await response.json();
      this.context.loggedIn(
        userData.isValid,
        userData.personid,
        userData.firstname,
        userData.lastname
      );
      localStorage.setItem("is_logged", userData.isValid);
      localStorage.setItem("personid", userData.personid);
      localStorage.setItem("firstname", userData.firstname);
      localStorage.setItem("lastname", userData.lastname);

      this.setState({
        couldNotfindLogin: true,
        redirect: true
      });
    }

    if (reply.status !== 200) {
      this.context.loggedIn(false);
      console.log(this.context);
    }
    this.setState({
      couldNotfindLogin: false
    });
  };
  componentWillUnmount() {
    this.mounted = false;
  }

  render() {
    let { couldNotfindLogin } = this.state;
    if (this.state.redirect === true) {
      return <Redirect to="/" />;
    }

    return (
      <>
        <section className="container">
          <div className="row">
            <div className="col-sm-12 col-md-12 col-lg-12 login-section">
              <div className="form login-box">
                <form className="form-login Login" onSubmit={this.handleSubmit}>
                  <label htmlFor="Email">
                    <p className="login-header">
                      Login to see your past, present, and future self.
                    </p>
                    <input
                      className="login-input"
                      id="Email"
                      type="text"
                      name="email"
                      onChange={this.handleChange}
                      placeholder="Email Address"
                      required
                    />
                    {!couldNotfindLogin ? (
                      <p className="FindYou">We could not find your account </p>
                    ) : (
                      " "
                    )}
                  </label>
                  <label htmlFor="Password">
                    <input
                      className="login-input"
                      id="Password"
                      type="password"
                      name="password"
                      onChange={this.handleChange}
                      placeholder="Password"
                      required
                    />
                  </label>
                  <button className="login-button" type="submit">
                    Login
                  </button>
                </form>
              </div>
            </div>
          </div>
        </section>
      </>
    );
  }
}

我发现了一些阻碍您的错误。我会尽量简短地回答这个问题:

  • 允许 Provider 处理与高级 statelocalStorage 相关的所有事情。仅使用与组件相关的本地状态。此外,由于 setState() 是异步的,因此请避免在另一个异步函数之后立即设置它。虽然你确实有一个 _isMounted class 属性,但它不会在异步函数(获取请求)解析时被检查,因此如果你将用户重定向到另一条路线,而React 尝试更新组件状态,然后您将收到上述警告。简而言之,在使用 setState 之前在异步函数中使用 _isMounted,或者干脆避免使用 setState(参见下面的示例)。
  • handleChange class 字段只需要一个 [e.target.name]: e.target.value 语句。有两个是不必要的。您可以使用 object destructuring 将其简化为:[name]: value(参见下面的示例)。
  • 提供给 Route 的任何 component 都可以访问某些 route props。在 props 中有一个 history 对象,它包含几个方法——其中有一个 push 方法。您可以使用此 push 方法将用户重定向到另一个 url:this.props.history.push("url")。同样,默认情况下,它提供给位于 react-router-dom Route.[=63= 中的 component ]
  • async/await 仅适用于 promises. Therefore, you'll want to await the initial fetch response and then await the fetch response.json() promise. The first promise, returns a Stream object, while the second promise returns a JSON object (this is by design, other libraries, like axios,跳过第二个承诺)。因此,const reply = await response; 表达式不是必需的,因为它不是在等待 promise 的解析,而是只是将响应的 Stream 对象 设置为 reply 变量。
  • 最后,如果您计划在同一执行周期内动态更新或更改此变量,则仅对变量使用 let;否则,只需使用 const。这变得尤为重要,因为 stateprops 应该始终是 immutable 以便 React 知道它们何时或是否已更新(通过另一个组件或 setState)。简而言之,避免将 let 用于 state/prop 表达式,例如:let { password } = this.state.

工作示例(我在模仿 fetch 响应承诺,但您可以通过查看 src/api/index.js 文件——例如,您可以通过提交密码 invalid):

来触发错误响应