У кого-нибудь есть такая проблема?«Чтобы исправить, отмените все подписки и асинхронные задачи в методе componentWillUnmount» - PullRequest
0 голосов
/ 01 декабря 2018

Я сталкиваюсь со следующим сообщением при реализации приложения реакции.У кого-нибудь есть такая же проблема, как эта?

Предупреждение. Невозможно выполнить обновление состояния React для отключенного компонента.Это не работает, но это указывает на утечку памяти в вашем приложении.Чтобы исправить, отмените все подписки и асинхронные задачи в методе componentWillUnmount.в ProductList (в App.js: 44)

Моя страница входа является компонентом ProductList.После загрузки страницы входа, если я щелкаю LogOut в заголовке, я сталкиваюсь с этим сообщением.У кого-нибудь есть совет для этого?

Поэтому я сослался на несколько ответов, например,

Невозможно вызвать setState (или forceUpdate) для размонтированного компонента.Это не работает, но это указывает на утечку памяти в вашем приложении

Однако я не могу решить, почему это происходит.: (

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 голосов
/ 01 декабря 2018

Проблема внутри componentDidUpdate, и поскольку this.setState является асинхронным действием, появляется ошибка.

Как это происходит?

  1. Действие по выходу из системы
  2. ProductList componentDidUpdate не защищен никаким условием, и this.setState вызывается бесконечно.
  3. ProductList componentWillUnmount запускает
  4. Асинхронное действие this.setState({ products: products }) из componentDidUpdate пытается обновить состояние
  5. Появляется ошибка

Чтобы устранить проблему, добавьте условие в componentDidUpdate.

Официальные документы о componentDidUpdate

Вы можете немедленно вызвать setState () в componentDidUpdate (), но учтите, что он должен быть заключен в условие, как в примере выше, иначе вы вызовете бесконечный цикл.Это также вызвало бы дополнительный повторный рендеринг, который, хотя и невидим для пользователя, может повлиять на производительность компонента.Если вы пытаетесь «отразить» какое-либо состояние для опоры, идущей сверху, рассмотрите возможность использования опоры напрямую.Узнайте больше о том, почему копирование реквизита в состояние приводит к ошибкам.

Пример решения:

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