首页 > 解决方案 > 有人有这个问题吗?“修复,取消componentWillUnmount方法中的所有订阅和异步任务”

问题描述

我在实现反应应用程序时遇到以下消息。有没有人有同样的问题?

警告:无法对未安装的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要修复,请取消 componentWillUnmount 方法中的所有订阅和异步任务。在 ProductList 中(在 App.js:44)

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

所以我提到了几个答案,比如

无法在未安装的组件上调用 setState(或 forceUpdate)。这是一个无操作,但它表明您的应用程序中存在内存泄漏

但是我无法解决它,为什么会发生这种情况。:(

应用程序.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;

产品列表.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;

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

标签: reactjslifecycle

解决方案


问题在内部componentDidUpdate,因为this.setState是异步操作,所以会出现错误。

它是如何发生的?

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

要解决您的问题,请在componentDidUpdate.

关于 componentDidUpdate 的官方文档

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

示例解决方案:

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

推荐阅读