How to invoke event in React child when it loads

OK, I have another fundamental React question.

The parent component loads data from a CSV file. Then the parent component manipulates the data to build an array of arrays of x,y coordinates in the form of updated state variable hhPerDay via the event countHH().

I want to take this state, pass it down as props to child component, and then use the data to create a graph.

How I am doing that right now:

  1. A button in child component is triggering the countHH() event.
  2. That builds the variable hhPerDay in parent component.
  3. Parent component passes hhPerDay down to child component.
  4. Graph is built using data from hhPerDay.

But what am I supposed to be doing if all I want is for the chart to render with data already there. Where can I invoke the countHH() event? I am guessing in the parent but where exactly…and how…if I don’t want to wait for a click or any other user input…?

All my code is below.

Thanks.

import React, { Component } from "react";
import VicAreaChart from "./vicAreaChart";

class DataParser extends Component {
  constructor(props) {
    super(props);
    this.state = {
      data: [],
      hhPerDay: []
    };
    this.updateData = this.updateData.bind(this);
    this.countHH = this.countHH.bind(this);
  }

  componentDidMount() {
    // Your parse code, but not seperated in a function
    var csvFilePath = require("../data/household_characteristics.csv");
    var Papa = require("papaparse/papaparse.min.js");
    Papa.parse(csvFilePath, {
      header: true,
      download: true,
      skipEmptyLines: true,
      // Here this is also available. So we can call our custom class method
      complete: this.updateData
    });
  }

  updateData(result) {
    const data = result.data;
    // Here this is available and we can call this.setState (since it's binded in the constructor)
    this.setState({ data: data }); // or shorter ES syntax: this.setState({ data });
  }

  countHH() {
    let hhPerDay = this.state.hhPerDay;
    let oldDate = [];
    for (let i = 0; i < 10; i++) {
      //change back to i<this.state.data.length
      let currDate = this.state.data[i].survey;
      console.log(currDate);
      //if currDate does not exist in oldDate array, THEN:()
      const hhCount = this.state.data.filter(each => each.survey === currDate)
        .length;
      let pair = [hhCount, currDate];
      hhPerDay.push(pair);
      console.log(hhPerDay);
      oldDate.push(currDate);
    }
    this.setState({ hhPerDay: hhPerDay });
  }

  render() {
    // Your render function
    console.log(this.state.data[0]);

    return (
      <div>
        <VicAreaChart
          hhData={this.state.data}
          hhPerDay={this.state.hhPerDay}
          countHH={this.countHH}
        />
      </div>
    );
  }
}

export default DataParser;

and:

import React from "react";
import { VictoryArea, VictoryChart, VictoryTheme } from "victory";

const VicAreaChart = props => {
  const { hhData, hhPerDay, countHH } = props;
  console.log(hhData[0]);
  console.log(hhPerDay);
  return (
    <div>
      <button onClick={countHH}>Click Me To Load Data</button>
      <p>This is not a drill</p>
      <VictoryChart theme={VictoryTheme.material}>
        <VictoryArea
          style={{ data: { fill: "#c43a31" } }}
          data={hhPerDay}
          x={1}
          y={0}
        />
      </VictoryChart>

      {/* {hhData.map(each => (
        <p key={each.hhid}>{each.survey}</p>
      ))} */}
    </div>
  );
};

export default VicAreaChart;

Have you tried calling the method from the updateData method, passing the data to countHH? or combining these two functions? Do you need two different states?

Example: https://codepen.io/jnmorse/pen/KLGVxg

1 Like

Thanks @jnmorse.

Is this the right division of labor between updateData() and countHH()?

There’s at least 1 second delay between the page (with axes) loading and the red area chart populating…is that normal? Thank you.

  updateData(result) {
    const data = result.data;
    const hhPerDay = this.countHH(data);
    // Here this is available and we can call this.setState (since it's binded in the constructor)
    this.setState({ data: data, hhPerDay: hhPerDay }); // or shorter ES syntax: this.setState({ data });
  }

  countHH(inputData) {
    let hhPerDay = this.state.hhPerDay;
    let oldDate = [];
    for (let i = 0; i < 10; i++) {
      //change back to i<this.state.data.length
      let currDate = inputData[i].survey;
      //if currDate does not exist in oldDate array, THEN:()
      const hhCount = inputData.filter(each => each.survey === currDate).length;
      let pair = [hhCount, currDate];
      hhPerDay.push(pair);
      oldDate.push(currDate);
    }
    return hhPerDay;
  }

When loading any asynchronous data, its going to take some time. So yes the brief moment or so before the data becomes avaible is normal, partly why in the example I put the part with loading… to show something before the data is avaible.

1 Like

Hello, I am trying to update a child component based on action in another child component and I think I am going round in circles as far as what logic needs to come first.

Starting point:

  1. Web page shows 2 charts (sibling components) - one is a time series area chart (number of households surveyed on each day) and the other is a bar chart (breakdown of the households surveyed as far as how many rent, own etc in the time period specified by area chart).
  2. I have a brush element on the area chart. As the brush moves across the area chart (by user action), the bar chart data should change to reflect the new time period.

My logic so far:
componentDidMount()

  • pulls in data from xls file and then triggers updateData()

updateData()

  • uses the data as input to trigger 2 other events:
    • countHH() and countRentLeaseBuy()
    • results from each of those events set state variables for both charts described above

countHH()

  • Loops through dataset to add up the surveys completed on a particular date
  • Returns array of objects:
    [{numberX: "date1"}, {numberY: "date2"}, {numberZ: "date3"} etc]

countRentLeaseBuy()

  • Loop through inputted dataset and add up the number of houses that are rented, etc
  • Returns array of arrays:
    [[RENT: numberA], [LEASE: numberB], [GMENT: numberC], [n/a: numberD] ]

handleBrush()

  • Filter dataset to only pull those that fall within a date range (although right now, it pulls as of a specific, hardcoded date b/c baby steps).
  • Triggers countRentLeaseBuy() with filtered data as input

So my question is - where should I be setting state versus returning a variable for another event to then use to set state…? I have tried both ways:

  1. Have countRentLeaseBuy() return variable which is used to set state in updateData(). The problem with this is that this event does not run all the time.
  2. Have countRentLeaseBuy() return variable and then also use that variable to set state itself. the problem with this is that when page first loads, the bar chart is pulling (what I can only assume is) data that comes as default to the chart element…?

I feel like this has something to do with how re-setting state triggers a re-rendering of page but I am not fully grasping how to think about this and more importantly, how to code it as needed.

All code:

import React, { Component } from "react";
import VicAreaChart from "./vicAreaChart";
import BarChart1 from "./barChart1";
import _ from "lodash";


class DataParser extends Component {
  constructor(props) {
    super(props);
    this.state = {
      data: [],
      hhPerDay: [],
      rentVsLease: []
    };
    this.updateData = this.updateData.bind(this);
    this.countHH = this.countHH.bind(this);
    this.handleBrush = this.handleBrush.bind(this);
  }

  componentDidMount() {
    var csvFilePath = require("../data/household_characteristics.csv");
    var Papa = require("papaparse/papaparse.min.js");
    Papa.parse(csvFilePath, {
      header: true,
      download: true,
      skipEmptyLines: true,
      complete: this.updateData
    });
  }

  //Need to run this with filtered data or full data, depending on what is needed.
  updateData(result) {
    const data = result.data;
    const hhPerDay = this.countHH(data);
    const rentVsLease = this.countRentLeaseBuy(data);
    this.setState({ data: data, hhPerDay: hhPerDay, rentVsLease: rentVsLease }); // or shorter ES syntax: this.setState({ data });
  }

  countHH(inputData) {
    let hhPerDay = this.state.hhPerDay;
    let oldDate = [];
    for (let i = 0; i < 200; i++) {
      //have to eventually loop through all of this.state.data = inputData
      let currDate = inputData[i].survey;
      if (oldDate.indexOf(currDate) === -1) {
        const hhCount = inputData.filter(each => each.survey === currDate)
          .length;
        let pair = [hhCount, currDate];
        hhPerDay.push(pair);
        oldDate.push(currDate);
      }
    }
    hhPerDay.sort((a, b) => new Date(a[1]) - new Date(b[1]));
    return hhPerDay;
  }

  //Can probably combine this with countHH?
  countRentLeaseBuy(inputData) {
    let rentVsLease = [];
    let oldStatus = [];
    for (let i = 0; i < 200; i++) {
      let currStatus = inputData[i].ownrent;
      if (oldStatus.indexOf(currStatus) === -1) {
        const count = inputData.filter(each => each.ownrent === currStatus)
          .length;
        let pair = { x: currStatus, y: count };
        rentVsLease.push(pair);
        oldStatus.push(currStatus);
      }
    }
    // console.log(rentVsLease);
    // return rentVsLease;
    this.setState({ rentVsLease: rentVsLease });
  }

  handleBrush(newDomain, brushProps) {
    // console.log(newDomain);
    // console.log(brushProps);
    const { rentVsLease, data } = this.state;
    const newData = data.filter(each => each.survey === "2016-07-01"); //EVentually, this has to equal domain of brush.
    this.setState({ data: newData });
    this.countRentLeaseBuy(data);
    //What dates do the newDomain.x values correspond to in hhData?
    //Calculate the number of HHs renting, leasing, gment housing, or n/a in between those dates.
    //Change the view of bar chart with new numbers
  }

  render() {
    return (
      <div>
        <VicAreaChart
          hhData={this.state.data}
          hhPerDay={this.state.hhPerDay}
          countHH={this.countHH}
          handleBrush={this.handleBrush}
        />
        <BarChart1 barData={this.state.rentVsLease} />
      </div>
    );
  }
}

export default DataParser;