反应:"warning: Can't perform a React state update on an unmounted component" - 尝试通过上下文更新<App />



My App 组件:

class App extends React.Component {
state = {
user: {},
statusMessage: null,
statusType: null,
setStatusMessage: () => {}
}
renderStatusMessage = () => {
if (this.state.statusMessage) {
return <div className={"alert alert-" + this.state.statusType}>{this.state.statusMessage}</div>
}
return null;
}
render() {
return (
<div className="App container">
<React.StrictMode>
<AppContext.Provider value={this.state}>
<BrowserRouter>
{this.renderStatusMessage()}
<IssueBrowser />
</BrowserRouter>
</AppContext.Provider>
</React.StrictMode>
</div>
);
}
}

我尝试在其中调用的组件this.context.setStatusMessage

export default class UserRegistration extends React.Component {
state = {
redirect: false }
static contextType = AppContext
registerUser = (event) => {
event.preventDefault()
// TODO: check if password matches confirmation, display error message
// if it doesn't
let payload = {
first_name: event.target.first_name.value,
last_name: event.target.last_name.value,
email_address: event.target.email_address.value,
password: event.target.password.value }
Users.create(payload)
.then(response => { 
this.context.setStatusMessage("success", "User successfully registered.")
this.setState({redirect: true}) })
.catch(error => { console.log(error) })
}
renderRedirect = () => {
if (this.state.redirect) {
return <Redirect to="/" />
}
}
render() {
return (
<form onSubmit={this.registerUser}>
{this.renderRedirect()}
<div className="form-group">
<label htmlFor="first_name">First Name:</label>
<input name="first_name" type="text" className="form-control" />
</div>
<div className="form-group">
<label htmlFor="last_name">Last Name:</label>
<input name="last_name" type="text" className="form-control" />
</div>
<div className="form-group">
<label htmlFor="email_address">Email Address:</label>
<input name="email_address" type="text" className="form-control" />
</div>
<div className="form-group">
<label htmlFor="password">Password:</label>
<input name="password" type="password" className="form-control" />
</div>
<div className="form-group">
<label htmlFor="password">Confirm Password:</label>
<input name="password" type="password" className="form-control" />
</div>
<div className="form-group">
<input className="btn btn-primary form-control" type="submit" value="Register" />
</div>
</form>
)
}
}

警告:

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 UserRegistration (at IssueBrowser.js:90)
in Route (at IssueBrowser.js:89)
in Switch (at IssueBrowser.js:88)
in MainSwitch (at IssueBrowser.js:117)

我认为<App />总是挂载的,因为它是根组件。

这是<IssueBrowser />的代码

export default class IssueBrowser extends React.Component {
constructor(props) {
super(props)
this.state = {
issues: [],
redirect: false,
loading: true,
noResponse: false
}
}
render() {
const NewIssueForm = (props) => {
return (
<form onSubmit={this.createIssue}>
<div className="form-group">
<label htmlFor="title">Title</label>
<input className="form-control" name="title" type="text" />
</div>
<div className="form-group">
<label htmlFor="body">Body</label>
<textarea name="body" className="form-control" rows="10"></textarea>
</div>
<input type="submit" className="btn btn-primary" />
</form>
)
}
const IssueDetail = (props) => {
let {id} = useParams()
let issue = this.state.issues.find(issue => issue.id === id)
if (issue) {
return (
<div>
<h1 className="card-title">{issue.title}</h1>
<p className="card-text">{issue.body}</p>
<button className="btn btn-primary" onClick={(e) => this.deleteIssue(issue.id)}>Delete</button>
</div>
)
} else {
return null
}
}
const IssueListItem = (props) => {
return (
<li className="list-group-item" key={props.issue.id}>
<Link to={"/issues/" + props.issue.id}>{props.issue.title}</Link>
</li>
)
}
const IssueList = (props) => {
if (this.state.issues && this.state.issues.length > 0) {
return (
<ul className="list-group list-group-flush">
{this.state.issues.map(issue => <IssueListItem key={issue.id} issue={issue} />)}
</ul>
)
} else if (!this.state.noResponse) {
return (
<div>
There are no issues. Aren't you lucky?
</div>
)
}
return null;
}
const MainSwitch = (props) => {
if (this.state.loading) {
return (
<div align="center">
<LoaderWidget />
</div>
)
} else {
return (
<Switch>
<Route exact path="/users/registration">
<UserRegistration />
</Route>
<Route exact path="/issues/create/">
{this.state.redirect ? <Redirect to="/" /> : null}
<NewIssueForm />
</Route>
<Route exact path="/issues/:id">
{this.state.redirect ? <Redirect to="/" /> : null}
<IssueDetail />
</Route>
<Route exact path="/">
<IssueList />
</Route>
</Switch>
)
}
}
return (
<div className="mt-5">
<this.Navigation />
<this.NoResponseFromServer noResponse={this.state.noResponse} />
<div className="p-3 border border-top-0 rounded-bottom">
<MainSwitch />
</div>
</div>
)
}
deleteIssue = (issue_id) => {
this.setState({redirect: true, loading: true})
Issues.delete(issue_id)
.then(response => { this.refreshIssues() })
}
createIssue = (event) => {
event.preventDefault()
let payload = {
title: event.target.title.value,
body: event.target.body.value }
this.setState({redirect: true, loading: true})
Issues.create(payload)
.then(response => { console.log(this.state); this.issueCreated() })
.catch(error => { console.log(error) })
}
issueCreated = () => {
this.refreshIssues();
}
setFlag = (flagKey, flagValue) => {
this.setState({[flagKey]: flagValue})
}
refreshIssues = () => {
Issues.getAll()
.then(issues => {
this.setState({issues, loading: false})
})
.catch(error => {
if (!error.response) { this.setFlag('noResponse', true) }
})
}
componentDidMount() {
this.refreshIssues()
}
componentDidUpdate(prevProps, prevState, snapshot) {
// Have to clear redirect flag here. The redirect happens when the HTML
// is re-rendered, because redirects happen via <Redirect /> tag. but we
// were clearing the flag before any re-rendering happened, meaning
// <Redirect /> was never even being rendered in the first place. So
// after a re-render takes place, check to see if redirect = true, then
// clear it. Doing it this way gives the <Redirect /> component a chance
// to be rendered before being cleared.
if (prevState.redirect) {
this.setState({redirect: false})
}
}

NoResponseFromServer = (props) => {
if (this.state.noResponse) {
return (
<div className="alert alert-danger">
No response received from server. Is it running?
</div>
)
} else {
return null;
}
}
Navigation = (props) => {
return (
<nav className="nav nav-tabs">
<li className="nav-item"><NavLink to="/" exact={true} className="nav-link" activeClassName="active">All</NavLink></li>
<li className="nav-item"><NavLink to="/issues/create/" className="nav-link" activeClassName="active">New</NavLink></li>
<li className="nav-item"><NavLink to="/users/registration/" className="nav-link" activeClassName="active">Register</NavLink></li>
</nav>
)
}
}
function LoaderWidget(props) {
return (
<img src={loader_gif} width="50" alt="loading..." />
)
}

您在组件中有一个异步调用,例如Users.create(payload),然后设置组件的状态。您只能在组件仍呈现/挂载时设置状态。为此,您需要跟踪组件挂载状态。您可以通过以下方式执行此操作:

componentDidMount() { 
this._isMounted = true;
}
componentWillUnmount() {
this._isMounted = false;
}

并且仅在this._isMounted时设置状态。

相关内容

最新更新