问题
I have a react component with 3 tabs, and each tab contains a datatable which will be populated with the API call result on the basis of active tab. So everytime there will be a parameter which I pass with API call and display data accordingly.
This is working great, I am getting data when I switch through tabs, but when I try to search from the datatable and then click on another tab I get the error as-
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.
Below is my sample code:
component.jsx
import React, { Component } from "react";
import axios from "axios";
class Details extends Component {
constructor() {
super();
this.state = {
data: [],
flag: 0
};
}
componentDidMount() {
const params = new FormData();
params.append("status", "upcoming");
axios.post("details/", params).then(res => {
if (res.data.result === 1) {
this.setState({ data: res.data.data, flag: 1 });
}
});
}
handleClick = event => {
const params = new FormData();
params.append("status", event.target.getAttribute("data-value"));
axios.post("details/", params).then(res => {
if (res.data.result === 1) {
this.setState({ data: res.data.data, flag: 1 });
}
});
};
render() {
return (
<div className="col-md-9 col-sm-9 col-xs-12">
<div className="right_panel">
<h2>Listing</h2>
<div className="responsive-tabs text-center ">
<ul className="nav nav-tabs" role="tablist">
<li role="presentation" className="active">
<a
href="#Upcoming"
data-value="upcoming"
onClick={this.handleClick}
aria-controls="Upcoming"
role="tab"
data-toggle="tab"
>
Upcoming
</a>
</li>
<li role="presentation" className="">
<a
href="#Current"
data-value="active"
onClick={this.handleClick}
aria-controls="Current"
role="tab"
data-toggle="tab"
>
Current
</a>
</li>
<li role="presentation" className="">
<a
href="#past"
data-value="past"
onClick={this.handleClick}
aria-controls="past"
role="tab"
data-toggle="tab"
>
Past
</a>
</li>
</ul>
<div
id="tabs-content"
className="tab-content panel-group table-responsive"
>
<div className="panel-heading" role="tab" id="heading2">
<a
href="#Upcoming"
className="text-left collapsed textuppercase"
role="button"
data-toggle="collapse"
data-parent="tabs-content"
aria-expanded="true"
aria-controls="Upcoming"
>
<i className="fas fa-list-ul" /> Upcoming
<i className="fas fa-chevron-down pull-right" />
</a>
</div>
<div
id="Upcoming"
role="tabpanel"
className="tab-pane active panel-collapse collapse in"
aria-labelledby="heading2"
>
<table
id="first_Datatable"
className="display"
style={{ width: "100%" }}
>
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Open</th>
<th>Close</th>
<th>Listed</th>
<th>Price</th>
<th>Size</th>
</tr>
</thead>
{this.state.flag === 1 ? (
<tbody>
{this.state.data.map(d => (
<tr key={d.ipo_details_id}>
<td className="text-center">{d.ipo_details_id}</td>
<td>
<a href="#" title="">
{d.name}
</a>
</td>
<td>{d.open_date}</td>
<td>{d.close_date}</td>
<td>{d.size}</td>
<td>{d.listing}</td>
<td>{d.price}</td>
</tr>
))}
</tbody>
) : null}
</table>
</div>
<div className="panel-heading" role="tab" id="heading3">
<a
href="#Current"
className="collapsed text-left textuppercase"
role="button"
data-toggle="collapse"
data-parent="tabs-content"
aria-expanded="true"
aria-controls="Current"
>
<i className="fas fa-list-ul" /> Current{" "}
<i className="fas fa-chevron-down pull-right" />
</a>
</div>
<div
id="Current"
role="tabpanel"
className="tab-pane panel-collapse collapse"
aria-labelledby="heading3"
>
<table
id="second_Datatable"
className="display"
style={{ width: "100%" }}
>
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Open</th>
<th>Close</th>
<th>Listed</th>
<th>Price</th>
<th>Size</th>
</tr>
</thead>
{this.state.flag === 1 ? (
<tbody>
{this.state.data.map(d => (
<tr key={d.ipo_details_id}>
<td className="text-center">{d.ipo_details_id}</td>
<td>
<a href="#" title="">
{d.name}
</a>
</td>
<td>{d.open_date}</td>
<td>{d.close_date}</td>
<td>{d.size}</td>
<td>{d.listing}</td>
<td>{d.price}</td>
</tr>
))}
</tbody>
) : null}
</table>
</div>
<div className="panel-heading" role="tab" id="heading3">
<a
href="#past"
className="collapsed text-left textuppercase"
role="button"
data-toggle="collapse"
data-parent="tabs-content"
aria-expanded="true"
aria-controls="past"
>
{" "}
<i className="fas fa-list-ul" /> Past{" "}
<i className="fas fa-chevron-down pull-right" />
</a>
</div>
<div
id="past"
role="tabpanel"
className="tab-pane panel-collapse collapse"
aria-labelledby="heading3"
>
<table
id="third_Datatable"
className="display"
style={{ width: "100%" }}
>
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Open</th>
<th>Close</th>
<th>Listed</th>
<th>Price</th>
<th>Size</th>
</tr>
</thead>
{this.state.flag === 1 ? (
<tbody>
{this.state.data.map(d => (
<tr key={d.ipo_details_id}>
<td className="text-center">{d.ipo_details_id}</td>
<td>
<a href="#" title="">
{d.name}
</a>
</td>
<td>{d.open_date}</td>
<td>{d.close_date}</td>
<td>{d.size}</td>
<td>{d.listing}</td>
<td>{d.price}</td>
</tr>
))}
</tbody>
) : null}
</table>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default Details;
I tried updating with componentWillUnmount, but didn't solved.
I am initializing datatable in index.html.
So what could be the possible solution for this.
回答1:
It looks like the problem here is that you have a data model which is getting stored in component state, but that component isn't sticking around very long. So you have several options:
- Keep the state in the component, and cancel the async request when the component unmounts. this can be done in axios using cancel tokens, which you would create on construction and trigger on unmount. This would also cancel the http request, so the server on the other side wouldn't have to respond to it. This might be the cleanest option, if halting any requests in progress when tabs are switched is the behavior you want.
- Just ignore the warning. That's up to you, if it is important to fix this or not. It doesn't seem to effect behavior. Morgan's answer effectively does this, and it's a perfectly viable solution, but it does defeat the purpose of the warning.
- Move the state out of the component (and pass it in as props or context). If the state is not stored in the component, then updating it after the component is unmounted is no longer a problem. As a side benefit, you don't have to re-fetch the state every time you mount the component, if you don't want to. You can keep the state in a higher level parent component that doesn't unmount and pass an update handler as well as the state to this component, or you can use something like Redux or MobX to handle state for you.
- Don't unmount the component when switching tabs. If you keep the component rendered, but just hide it with html and css, then the component will stay mounted. There are also other fancy tricks you can do such as keeping the component mounted but in an "offscreen" document fragment. The benefits of this is that it can speed up rendering, but it can use more memory, and is also kind of a react anti pattern.
Which option you choose depends on what you want to accomplish.
回答2:
It seems the issue stems from the POST request you send in componentDidMount.
componentDidMount() {
const params = new FormData();
params.append("status", "upcoming");
axios.post("details/", params).then(res => {
if (res.data.result === 1) {
this.setState({ data: res.data.data, flag: 1 });
}
});
}
What if you modify the if
statement preceding your state update with something like this?
if (res.data.result === 1 && this.state) {
That way it will only try to update state if this.state
exists? Simple and elegant enough.
Let me know if that doesn't work!
来源:https://stackoverflow.com/questions/53945879/reactjs-could-not-update-state-when-used-with-tabs