ReactJS не удалось обновить состояние при использовании с вкладками
У меня есть компонент реагирования с 3 вкладками, и каждая вкладка содержит таблицу данных, которая будет заполнена результатом вызова API на основе активной вкладки. Так что каждый раз будет параметр, который я передаю с вызовом API и отображаю данные соответственно.
Это прекрасно работает, я получаю данные, когда переключаюсь между вкладками, но когда я пытаюсь выполнить поиск из таблицы данных, а затем нажимаю на другую вкладку, я получаю сообщение об ошибке
Невозможно выполнить обновление состояния React для неустановленного компонента. Это не работает, но это указывает на утечку памяти в вашем приложении. Чтобы исправить, отмените все подписки и асинхронные задачи в методе componentWillUnmount.
Ниже приведен мой пример кода:
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;
Я пытался обновить с помощью componentWillUnmount, но не решил.
Я инициализирую данные в index.html.
Так что может быть возможным решением для этого.
2 ответа
Похоже, проблема в том, что у вас есть модель данных, которая хранится в состоянии компонента, но этот компонент не остается слишком долго. Итак, у вас есть несколько вариантов:
- Сохраните состояние в компоненте и отмените асинхронный запрос, когда компонент размонтируется. это можно сделать в axios, используя токены отмены, которые вы создадите при создании и сработаете при размонтировании. Это также отменит запрос http, поэтому серверу на другой стороне не придется отвечать на него. Это может быть самым чистым вариантом, если вам нужно остановить выполнение любых запросов при переключении вкладок.
- Просто игнорируйте предупреждение. Вам решать, важно это исправить или нет. Кажется, это не влияет на поведение. Ответ Моргана эффективно делает это, и это вполне жизнеспособное решение, но оно побеждает цель предупреждения.
- Переместите состояние из компонента (и передайте его как реквизит или контекст). Если состояние не сохраняется в компоненте, то обновление его после размонтирования компонента больше не является проблемой. Дополнительным преимуществом является то, что вам не нужно повторно выбирать состояние каждый раз, когда вы монтируете компонент, если вы этого не хотите. Вы можете сохранить состояние в родительском компоненте более высокого уровня, который не размонтируется и передать обработчик обновления, а также состояние этому компоненту, или вы можете использовать что-то вроде Redux или MobX для обработки состояния за вас.
- Не отключайте компонент при переключении вкладок. Если вы сохраняете компонент визуализированным, но просто скрываете его с помощью html и css, то компонент остается подключенным. Есть и другие хитрые уловки, которые вы можете сделать, например, держать компонент смонтированным, но в "закадровом" фрагменте документа. Преимущества этого в том, что он может ускорить рендеринг, но он может использовать больше памяти, а также является своего рода противодействующим паттерном.
Какой вариант вы выберете, зависит от того, чего вы хотите достичь.
Кажется, проблема связана с отправкой POST-запроса в 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 });
}
});
}
Что делать, если вы измените if
заявление, предшествующее обновлению вашего состояния чем-то вроде этого?
if (res.data.result === 1 && this.state) {
Таким образом, он будет пытаться обновить состояние, только если this.state
существует? Просто и достаточно элегантно.
Дайте мне знать, если это не сработает!