有人遇到这个问题吗? “要修复,请取消componentWillUnmount方法中的所有订阅和异步任务”

时间:2018-12-01 20:23:40

标签: reactjs lifecycle

在实施react应用程序时遇到以下消息。有人有这样的问题吗?

警告:无法在已卸载的组件上执行React状态更新。这是空操作,但它表明应用程序中发生内存泄漏。要解决此问题,请在componentWillUnmount方法中取消所有订阅和异步任务。在ProductList中(在App.js:44)

我的输入页面是ProductList组件。加载条目页面后,如果单击标题中的LogOut,则会遇到该消息。有人对此有建议吗?

所以我为此提到了几个答案

Can't call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application

但是我无法解决它,为什么会这样。 :(

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;

如果您提供一些建议,那将是很大的帮助。

1 个答案:

答案 0 :(得分:4)

问题出在componentDidUpdate内部,并且由于this.setState是异步操作,因此出现错误。

它是怎么发生的?

  1. 发生注销操作
  2. ProductList componentDidUpdate不受任何条件的保护,this.setState被无限调用。
  3. ProductList componentWillUnmount触发器
  4. this.setState({ products: products })中的异步componentDidUpdate操作尝试更新状态
  5. 出现错误

要解决您的问题,请在componentDidUpdate内添加一个条件。

官方文档about componentDidUpdate

  

您可以在componentDidUpdate()中立即调用setState(),但请注意   必须将其包装在上面示例中所示的条件下,或者   您将导致无限循环。这也会造成额外的   重新渲染虽然对用户不可见,但可能会影响   组件性能。如果您尝试将某些状态“镜像”到   道具来自上方,请考虑直接使用道具。读   有关为何将道具复制到状态会导致错误的更多信息。

示例解决方案:

componentDidUpdate(prevProps) {
   // Please do not forget to compare props
   if (this.props.somethingChanged !== prevProps.somethingChanged) {
     // this.setState logic here
   }
}