只能更新已安装或安装的组件。

时间:2018-04-10 23:40:45

标签: javascript reactjs

目前处理个人项目并且我收到此错误

Warning: Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op. Please check the code for the Graph component.

我的App component.也遇到了同样的问题我已经对这个问题进行了一些阅读,但我对于我的代码具体问题并不明智。

非常感谢任何见解。

这是running project (with sourcecode) on CodeSnadbox.io的链接,我也链接了下面的违规代码。

以下是Graph Component

import React, { Component } from "react";
import { render } from "react-dom";
import { Line, Doughnut, Bar } from "react-chartjs-2";
import moment from "moment";
import PropTypes from "prop-types";

import styleConstants from "../misc/style_constants.js";

class Graph extends Component {
  constructor(props) {
    super(props);
    this.state = {
      label: "default",
      dataset: [],
      labels: []
    };
  }

  /**
   * https://min-api.cryptocompare.com/ for documentation
   */
  async getHistoryData(ticker = "BTC", currency = "USD", filter = "close") {
    try {
      let response = await fetch(
        `https://min-api.cryptocompare.com/data/histoday?fsym=${ticker}&tsym=${currency}&limit=60&aggregate=3&e=CCCAGG`
      );
      const responseJson = await response.json();
      const dataset = responseJson.Data.map(data => {
        return data[filter];
      });
      const labels = responseJson.Data.map(data => {
        return moment(new Date(data.time * 1000)).format("MMM Do YY");
      });

      this.setState({ dataset: dataset });
      this.setState({ labels: labels });
    } catch (error) {
      console.log(error);
    }
  }

  componentDidMount() {
    const { ticker, currency, filter } = this.props;
    this.getHistoryData(ticker, currency, filter);
  }

  render() {
    const { label, graphType } = this.props;
    const { dataset, labels } = this.state;

    const options = {
      legend: {
        fontColor: styleConstants.get("Dark")
      },
      scales: {
        yAxes: [
          {
            ticks: {
              fontColor: styleConstants.get("Light"),
              beginAtZero: true,
              callback: function(value, index, values) {
                if (parseInt(value) >= 1000) {
                  return (
                    "$" + value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
                  );
                } else {
                  return "$" + value;
                }
              }
            }
          }
        ],
        xAxes: [
          {
            ticks: {
              fontColor: styleConstants.get("Light"),
              fontSize: 10,
              stepSize: 1,
              beginAtZero: true
            }
          }
        ]
      }
    };

    const data = {
      labels: labels,
      datasets: [
        {
          label: label,
          fill: true,
          lineTension: 0.1,
          backgroundColor: styleConstants.get("Medium"),
          borderColor: styleConstants.get("Medium"),
          borderCapStyle: "butt",
          borderDash: [],
          borderDashOffset: 0.0,
          borderJoinStyle: "miter",
          pointBorderColor: styleConstants.get("Light"),
          pointBackgroundColor: "#fff",
          pointBorderWidth: 1,
          pointHoverRadius: 5,
          pointHoverBackgroundColor: "rgba(75,192,192,1)",
          pointHoverBorderColor: "rgba(220,220,220,1)",
          pointHoverBorderWidth: 2,
          pointRadius: 1,
          pointHitRadius: 10,
          data: dataset
        }
      ]
    };
    return <Line data={data} options={options} />;
    // switch (graphType) {
    //   case "line":
    //     return <Line data={data} options={options} />;
    //     break;
    //   case "bar":
    //     return <Bar data={data} options={options} />;
    //     break;
    //   case "doughnut":
    //     return <Doughnut data={data} options={options} />;
    //     break;
    //   default:
    //     return null;
    // }
  }
}

Graph.propTypes = {
  label: PropTypes.string,
  graphType: PropTypes.string
};

Graph.defaultProps = {
  label: "Default String",
  graphType: "Default String"
};

export default Graph;

以下是App Component

import React, { Component } from "react";
import { render } from "react-dom";

import styled, { css } from "styled-components";

import styleConstants from "../misc/style_constants.js";

import Overview from "../components/Overview";
import Panel from "../components/Panel";
import Table from "../components/Table";

import Options from "./Options";
import Graph from "./Graph";

export default class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      selectedTicker: "BTC",
      currency: "USD",
      tickers: [],
      overview: []
    };
    this.updateTicker = this.updateTicker.bind(this);
    this.createGraph = this.createGraph.bind(this);
  }

  updateTicker(selectedValue) {
    const { value } = selectedValue;
    this.setState({ selectedTicker: value });
  }

  async getTickers() {
    try {
      const response = await fetch('https://api.coinmarketcap.com/v1/ticker/')
      const responseJSON = await response.json();
      this.setState({ tickers: responseJSON });
    } catch (error) {
      console.log("App getTickers() ", error);
    }
  }

  async getOverviewData() {
    try {
      const response = await fetch(`https://api.coinmarketcap.com/v1/global/?convert=${this.state.currency}`)
      const responseJSON = await response.json();
      this.setState({ overview: responseJSON });
    } catch (error) {
      console.log("App getOverviewData() ", error);
    }
  }

  componentDidMount() {
    this.getTickers();
    this.getOverviewData();
  }

  createGraph(ticker = "", currency = "", graphType = "", label = "", filter = "") {
    return (
      <Graph
        filter={filter}
        ticker={ticker}
        currency={currency}
        graphType={graphType}
        label={label}
      />
    )
  }

  render() {
    const { selectedTicker, currency } = this.state;
    const Container = styled.div`
      input:focus,
      select:focus,
      textarea:focus,
    `;

    const Title = styled.h1`
      text-align: center;
      color: ${styleConstants.get('Yellow')};
    `;

    const LightSpan = styled.span`
      font-weight: 200;
    `;

    return (
      <Container>
        <Title>
          Coin:<LightSpan>Dash</LightSpan>
        </Title>
        <Overview {...this.state.overview} />
        <Options
          selectedValue={this.state.selectedTicker}
          values={this.state.tickers.map(data => {
            return data.symbol;
          })}
          labels={
            this.state.tickers.map(data => {
              return data.id;
            })
          }
          updateTicker={this.updateTicker} />
        <Panel label={"Price Action"} content={this.createGraph(selectedTicker, currency, 'line', "Close", "close")} />
        <Panel label={"Highest Price"} content={this.createGraph(selectedTicker, currency, 'bar', "High", "high")} />
        <Panel label={"Lowest Price"} content={this.createGraph(selectedTicker, currency, 'bar', "Low", "low")} />
        <Panel label={"Top Ten List"} content={
          <Table header={["Rank", "Name", "Price", "Change(24 Hour)"]} collection={this.state.tickers} />

        } />

      </Container>
    );
  }
}

1 个答案:

答案 0 :(得分:1)

问题是由render App方法中定义的无状态功能组件引起的。如果在App类之外定义以下函数,则会修复错误:

const Container = styled.div`
      input:focus,
      select:focus,
      textarea:focus,
    `;

const Title = styled.h1`
      text-align: center;
      color: ${styleConstants.get('Yellow')};
    `;

const LightSpan = styled.span`
      font-weight: 200;
    `;

export default class App extends Component {
..

失败的原因是本地创建的SFC对每个渲染进行了更改,这导致它们卸载并重新安装,即使渲染保持不变。 Table组件中还有一些其他本地SFC,它们没有发出任何警告,但会造成不必要的重新安装。

更新:这是一个难题,但剩下的警告来自其中一个测试:

describe("App", () => {
  it("renders without crashing", () => {
    const div = document.createElement("div");
    ReactDOM.render(<App />, div);
    ReactDOM.unmountComponentAtNode(div);
  });
});

在异步操作完成之前卸载组件时,这是有道理的。

这是一个工作沙箱:https://codesandbox.io/s/24o6vp4rzp(我还删除了content={..}中的箭头功能,因为它需要是一个值)