我收到错误消息警告:无法对未安装的组件执行 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
处理与高级 state
和 localStorage
相关的所有事情。仅使用与组件相关的本地状态。此外,由于 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
。这变得尤为重要,因为 state
和 props
应该始终是 immutable 以便 React 知道它们何时或是否已更新(通过另一个组件或 setState
)。简而言之,避免将 let
用于 state/prop 表达式,例如:let { password } = this.state
.
工作示例(我在模仿 fetch
响应承诺,但您可以通过查看 src/api/index.js 文件——例如,您可以通过提交密码 invalid
):
来触发错误响应
我收到错误消息
警告:无法对未安装的组件执行 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
处理与高级state
和localStorage
相关的所有事情。仅使用与组件相关的本地状态。此外,由于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 toawait
the initial fetchresponse
and thenawait
the fetchresponse.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
。这变得尤为重要,因为state
和props
应该始终是 immutable 以便 React 知道它们何时或是否已更新(通过另一个组件或setState
)。简而言之,避免将let
用于 state/prop 表达式,例如:let { password } = this.state
.
工作示例(我在模仿 fetch
响应承诺,但您可以通过查看 src/api/index.js 文件——例如,您可以通过提交密码 invalid
):