I run into a following message while implementing react application. Does anyone have same issue like this?
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 ProductList (at App.js:44)
My entry page is ProductList component. After loading entry page, if I click the LogOut in the header, I run into that message. Does anyone have some advice for it?
So I referred to several answers for it like
Can't call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application
However I can't solve it, why this happens. :(
App.js
import React, { Component } from "react";
import { Route } from "react-router-dom";
import Header from "./Header";
import ProductList from "./ProductList";
import Login from "./Login";
import Logout from "./Logout";
import "./App.css";
class App extends Component {
constructor() {
super();
this.state = {
query: "",
isLoggedIn: false
};
this.handleLoginStatus = this.handleLoginStatus.bind(this);
this.handleLogoutStatus = this.handleLogoutStatus.bind(this);
this.setSearchKeyword = this.setSearchKeyword.bind(this);
}
handleLoginStatus() {
this.setState({ isLoggedIn: true });
}
handleLogoutStatus() {
this.setState({ isLoggedIn: false });
}
setSearchKeyword(query) {
this.setState({
query: query
});
}
render() {
return (
<div>
<Header setSearchKeyword={this.setSearchKeyword} />
<Route
path="/"
exact={true}
render={() => (
<ProductList
query={this.state.query}
isLoggedIn={this.state.isLoggedIn}
/>
)}
/>
<Route
path="/login"
render={() => (
<Login
isLoggedIn={this.state.isLoggedIn}
handleLoginStatus={this.handleLoginStatus}
/>
)}
/>
<Route
path="/logout"
render={() => (
<Logout
isLoggedIn={this.state.isLoggedIn}
handleLogoutStatus={this.handleLogoutStatus}
/>
)}
/>
</div>
);
}
}
export default App;
ProductList.js
import React, { PureComponent } from "react";
import { Table } from "react-bootstrap";
import axios from "axios";
class ProductList extends PureComponent {
constructor(props) {
super(props);
this.state = {
products: null,
loaded: false
};
}
// componentWillReceiveProps(nextProps) {
// console.log(nextProps.query);
// }
componentDidUpdate() {
const url =
"https://localhost/product/search?query=" + this.props.query;
const options = {
method: "GET",
headers: {
Authorization: "Bearer " + localStorage.getItem("auth-token")
},
url
};
axios(options)
.then(response => {
let products = response.data;
this.setState({ products: products });
})
.catch(error => {
console.log("axios error", error);
});
}
componentDidMount() {
const url =
"https://localhost/product/search?query=" + this.props.query;
const options = {
method: "GET",
headers: {
Authorization: "Bearer " + localStorage.getItem("auth-token")
},
url
};
axios(options)
.then(response => {
let products = response.data;
this.setState({ products: products, loaded: true });
})
.catch(error => {
console.log("axios error", error);
});
}
// ComponentWillUnmount() {
// this.isUnmounted = true;
// }
render() {
if (this.state.loaded) {
let columnNames = ["Num", "Name", "Indications", "features"];
let fieldNames = ["num", "name", "indications", "features"];
var tableHeaders = (
<tr>
{columnNames.map(column => {
return <th key={column}>{column}</th>;
})}
</tr>
);
var tableBody = this.state.products.map((product, i) => {
return (
<tr key={product + "_" + i}>
{fieldNames.map((field, j) => {
if (j === 0) {
return <td key={product.name + "_" + i + "_" + j}>{i + 1}</td>;
} else {
return (
<td key={product.name + "_" + i + "_" + j}>{product[field]}</td>
);
}
})}
</tr>
);
});
}
return (
<div>
<Table striped bordered condensed hover>
<thead>{tableHeaders}</thead>
<tbody>{tableBody}</tbody>
</Table>
</div>
);
}
}
export default ProductList;
If you give some advice, it will be great help.
The problem is inside componentDidUpdate
and because this.setState
is asynchronous action an error shows up.
How does it happen?
componentDidUpdate
is not guarded by any condition and this.setState
being called infinitely.componentWillUnmount
triggersthis.setState({ products: products })
action from componentDidUpdate
tries to update stateTo fix your problem add a condition inside componentDidUpdate
.
Official docs about componentDidUpdate
You may call setState() immediately in componentDidUpdate() but note that it must be wrapped in a condition like in the example above, or you’ll cause an infinite loop. It would also cause an extra re-rendering which, while not visible to the user, can affect the component performance. If you’re trying to “mirror” some state to a prop coming from above, consider using the prop directly instead. Read more about why copying props into state causes bugs.
Example solution:
componentDidUpdate(prevProps) {
// Please do not forget to compare props
if (this.props.somethingChanged !== prevProps.somethingChanged) {
// this.setState logic here
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With