ComponentDidUpdate导致无限渲染,即使包裹在条件中

时间:2018-11-01 17:36:24

标签: reactjs

我正在尝试从Google Analytics(分析)报告API中提取一些数据,并将数据显示在顶点图表库中。我成功地做到了这一点。但是,我现在想要过滤选项,以便用户在日期范围选择器反应包装器中选择某个日期时,将从API更新顶点图表数据。

我正在努力弄清楚如何更新我的数据,以便在生命周期方法中使用新状态更新状态?我想我正在做些次要的事情,我只是不知道那是什么。我查阅了有关生命周期方法的文档,并说要确保将其包装在我做过的条件内。但是,当满足else条件时,它将导致无限渲染。

这是我的代码:(我卡住的错误是componentWillUpdate生命周期方法),其他所有东西都可以正常工作。

import React from "react";

import Header from "../common/Header";
import Footer from "../common/Footer";

import moment from "moment";
import $ from "jquery";
import ApexCharts from "apexcharts";
import Chart from "react-apexcharts";
import DateRangePicker from "react-bootstrap-daterangepicker";

const VIEW_ID = "";

class Charts extends React.Component {
  constructor(props) {
    super(props);
    this.printResults = this.printResults.bind(this);
    this.pageViews = this.pageViews.bind(this);
    this.handleError = this.handleError.bind(this);
    this.state = {
      loading: true,
      filterstartDate: "",
      filterendDate: "",
      // Start Series Bar State

      ChartOne: {
        chart: {
          id: "ChartOne"
        },
        colors: ["#e31d1a"],
        xaxis: {
          categories: [],
          labels: {
            style: {
              colors: []
            }
          },
          title: {
            text: "Locations"
          }
        },
        yaxis: {
          labels: {
            style: {
              colors: []
            }
          },
          title: {
            text: "Count"
          }
        }
      },
      ChartOneSeries: [],
  }

  pageViews = async () => {
    window.gapi.client
      .request({
        path: "/v4/reports:batchGet",
        root: "https://analyticsreporting.googleapis.com",
        method: "POST",
        body: {
          reportRequests: [
            {
              viewId: VIEW_ID,
              dateRanges: [
                {
                  startDate: "7daysAgo",
                  endDate: "today"
                }
              ],
              metrics: [
                {
                  expression: "ga:pageviews"
                }
              ],
              dimensions: [
                {
                  name: "ga:country"
                }
              ],
              orderBys: [{ fieldName: "ga:pageviews", sortOrder: "DESCENDING" }]
            }
          ]
        }
      })
      .then(this.printResults, this.handleError);
  };

  componentDidMount() {
    $.getScript("https://apis.google.com/js/client:platform.js").done(() => {
      window.gapi.signin2.render("my-signin2", {
        scope: "profile email",
        width: 240,
        height: 50,
        longtitle: true,
        theme: "dark",
        onsuccess: this.pageViews,
        onfailure: this.handleError
      });
    });
  }

  //log the data
  printResults(response) {
    let pageviewLocation = [];
    let pageviewCount = [];
    let pageviewTotal = response.result.reports[0].data.totals[0].values[0];
    let totalComma = pageviewTotal
      .toString()
      .replace(/\B(?=(\d{3})+(?!\d))/g, ",");

    response.result.reports[0].data.rows.map(value => {
      //console.log(value.dimensions);
      pageviewLocation.push(value.dimensions[0]);
      pageviewCount.push(parseInt(value.metrics[0].values[0]));
    });

    //console.log(total);

    this.setState({
      loading: false,
      ChartOne: {
        title: {
          text: totalComma,
          align: "center",
          style: {
            fontSize: "20px"
          }
        },
        subtitle: {
          text: "Total Page Views",
          align: "center",
          style: {
            fontSize: "14px",
            cssClass: "apexcharts-yaxis-title"
          }
        },
        plotOptions: {},
        ...this.state.ChartOne,
        xaxis: {
          width: 1,
          ...this.state.ChartOne.xaxis,
          labels: {
            show: false,
            ...this.state.ChartOne.xaxis.labels,
            style: {
              ...this.state.ChartOne.xaxis.labels.style
            }
          },
          categories: pageviewLocation
        },
        yaxis: {
          min: 0,
          ...this.state.ChartOne.yaxis,
          labels: {
            //show: false,
            ...this.state.ChartOne.yaxis.labels,
            style: {
              ...this.state.ChartOne.yaxis.labels.style
            }
          }
        }
      },
      ChartOneSeries: [
        {
          name: "Total Page Views",
          data: pageviewCount
        }
      ]
    });
  }

  componentDidUpdate(prevProps, prevState) {
    if (this.state.filterstartDate === "" && this.state.filterendDate === "") {
      console.log("they are empty");
    } else {
      this.setState({
        // this fails immediately once the condition is met
        test: "success!"
      });
    }
  }

  Datepicker = async (event, picker) => {
    this.setState({
      filterstartDate: moment(picker.startDate._d).format("YYYY-MM-DD"),
      filterendDate: moment(picker.endDate._d).format("YYYY-MM-DD")
    });

    //console.log(this.state);
  };

  //or the error if there is one
  handleError(reason) {
    console.error(reason);
    console.error(reason.result.error.message);
  }

  render() {
    //console.log();

    return (
      <div className="containerfluid" id="fullWidth">
        <Header />
        <div className="container" id="chartContainer">
          <h1>Site Analytics</h1>
          <div className="row">
            <div className="col-md-12">
              <DateRangePicker
                startDate={moment().format("MM-DD-YYYY")}
                endDate={moment().format("MM-DD-YYYY")}
                onApply={this.Datepicker}
              >
                <button className="btn btn-info">
                  <i className="fas fa-filter">
                    <span
                      style={{
                        fontFamily: "Roboto, san-serif",
                        fontWeight: "normal",
                        padding: "5px"
                      }}
                    >
                      Filter Date
                    </span>
                  </i>
                </button>
              </DateRangePicker>
            </div>
          </div>
          <div className="row">
            <div className="col-md-4">
              {/* Chart One Line */}
              {this.state.loading ? (
                <React.Fragment>
                  <i className="fas fa-spinner fa-3x" id="loader" /> Please wait
                  ...!
                </React.Fragment>
              ) : (
                <div className="chartContainer">
                  <Chart
                    options={this.state.ChartOne}
                    series={this.state.ChartOneSeries}
                    type="line"
                    width={400}
                    height={300}
                  />
                </div>
              )}
            </div>
          </div>
          <div id="my-signin2" />
        </div>
        <Footer />
      </div>
    );
  }
}

export default Charts;

1 个答案:

答案 0 :(得分:4)

使用setState时,将再次触发生命周期。如果您未将filterstartDatefilterendDate设置为"",则将继续无限调用setState。

componentDidUpdate(prevProps, prevState) {
    if (this.state.filterstartDate === "" && this.state.filterendDate === "") {
      console.log("they are empty");
    } else {
      this.setState({
        filterstartDate: "",
        filterendDate: "",
        test: "success!"
      });
    }
  }