【问题标题】:How to make API requests correctly with Reactjs and Axios?如何使用 Reactjs 和 Axios 正确发出 API 请求?
【发布时间】:2019-08-17 01:28:46
【问题描述】:

我正在使用最新版本的 ReactJS 并使用 Axios 发出请求。但在我输入cancel() 函数之前,我收到了以下错误:

Warning: 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.
    in MenuPlaylist (at Sidebar/index.js:19)
    in aside (created by Context.Consumer)
    in StyledComponent (created by styled.aside)
    in styled.aside (at Sidebar/index.js:11)
    in Sidebar (at Search/index.js:16)
    in Search (created by Context.Consumer)

因为问题是组件一拆卸就泄漏了内存。但现在取消请求,我在控制台上收到以下消息:

而且我觉得很奇怪,好像系统有问题。

取消请求以避免内存泄漏的正确方法是什么?

组件:

import React, { Component } from "react";

// STYLES
import { Menu, Title } from "./styles";

// SERVICES
import { cancelAxiosRequest, getAllPlaylist } from "services/Api";

// SUBCOMPONENT'S
import { CreatePlaylist, CreatedPlaylist } from "components";

class MenuPlaylist extends Component {
  state = {
    data: []
  };

  // LIFE CYCLES
  componentDidMount() {
    this.consumeAPI();
  }

  componentWillUnmount() {
    cancelAxiosRequest("Request Canceled.");
  }

  // METHODS
  consumeAPI = () => {
    getAllPlaylist().then(({ data }) => {
      this.setState({ data: data });
    });
  };

  render = () => {
    return (
      <Menu>
        <Title>PLAYLISTS</Title>
        <CreatePlaylist />
        <CreatedPlaylist data={this.state.data} />
      </Menu>
    );
  };
}

export default MenuPlaylist;

AXIOS:

import axios from "axios";

const instance = axios.create({
  baseURL: "http://localhost:3001",
  timeout: 1000
});

let CancelToken = axios.CancelToken;
export let cancelAxiosRequest;

// GET'S
export function getNewReleases() {
  return instance.get("/newReleases");
}

export function getAllPlaylist() {
  return instance.get("/playlist", {
    cancelToken: new CancelToken(function executor(c) {
      cancelAxiosRequest = c;
    })
  });
}

export function getPlaylist(name) {
  return instance.get("/playlist", {
    params: {
      name: name
    }
  });
}

// POST'S
export function postNewPlaylist(id, name) {
  return instance.post("/playlist", {
    id: id,
    to: `/playlist/${id}`,
    name: name
  });
}

【问题讨论】:

    标签: reactjs axios


    【解决方案1】:

    根据documentation,“cancelling”意味着promise 抛出一个Cancel 错误,并带有你指定的消息。

    修复:

    // ...
    
    getAllPlaylist()
      .then(({ data }) => {
        this.setState({ data });
      })
      .catch(err => {
        if (axios.isCancel(err)) {
          return; // ignore
        }
        throw err; // or handle other errors
      });
    
    // ...
    

    虽然我觉得更改导出的值并不是使用取消令牌的最佳方式。检查您是否需要首先卸载组件,但如果它确实有时会被卸载,也许可以尝试类似

    export function getAllPlaylist(cancelToken) {
      return instance.get("/playlist", {
        cancelToken: cancelToken
      });
    }
    
    // ...
    
    getAllPlaylist(new CancelToken(c => this.cancel = c))
      .then(({ data }) => {
        this.setState({ data });
      });
    
    // ...
    
    componentWillUnmount() {
      this.cancel();
    }
    

    我不知道这是否 100% 理想,但这避免了组件的多个实例争夺单个变量。

    【讨论】:

    • 有没有更好的方法,在卸载组件时不会因为 API 调用而导致内存泄漏?
    【解决方案2】:

    你应该做一些类似的事情

    .catch((e) => {
            if (axios.isCancel(e)) return;
            setError(true);
          });
    

    因为取消不是错误

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-23
      • 1970-01-01
      • 1970-01-01
      • 2018-04-25
      • 2020-09-26
      • 1970-01-01
      • 1970-01-01
      • 2020-11-05
      相关资源
      最近更新 更多