ps1234
ps1234

Reputation: 161

Updating state of a react function component from parent class

Currently in the process of learning React and D3. I have the following code:

App.js

import React, {useRef, useEffect, useState, Component} from 'react';
// import logo from './logo.svg';
import './App.css';

import ChartWrapper from './ChartWrapper'

class App extends Component() {
  render() {
    return (
      <div id="app">
        <ChartWrapper data = {[10, 13, 9.5, 11, 12, 14, 8, 13, 15, 14]}/>
      </div>
    )
  }
}

export default App;

ChartWrapper.js

import React, {Component, useEffect, useState} from 'react';
import { useRef } from 'react';
import LineChart from './LineChart'

function ChartWrapper(props)  {
    const [data, setData] = useState(props.data);
    const svgRef = useRef();
    //let data = [10, 13, 9.5, 11, 12, 14, 8, 13, 15, 14];
    console.log("works");

    useEffect( () => {
        const chart = new LineChart(svgRef, data);
        console.log(data);
    }, [data]);

    function updateChart(value) {
        data.push(value);
    }

    return (
        <div>
            <svg ref={svgRef} height = {300} width = {500}></svg>
        <button onClick={() => setData(data.map(value => value + 5))}>Update Data</button>
        </div>

    );
}

export default ChartWrapper;

LineChart.js

import React, {Component} from 'react'
import {line, select, selectAll, axisBottom, ScaleLinear, scaleLinear} from 'd3';

class LineChart {
    constructor (parent, data) {
        this.svg = select(parent.current);
        this.data = data;
        this.line  = this.getLine();
        this.xScale = this.getXScale();
        this.yScale = this.getYScale();

        // Plot the chart
        this.plot();
    }

    getLine() {
        return line()
                .x((value, index) => this.xScale(index))
                .y(value => this.yScale(value));
    }

    getXScale() {
        return scaleLinear()
                .domain([0, this.data.length - 1])
                .range([0, this.svg.attr('width')]);
    }

    getYScale() {
        return scaleLinear()
                .domain([0, 20])
                .range([this.svg.attr('height'), 0]);
    }


    plot() {
        this.svg.selectAll('path')
                .data([this.data])
                .join('path')
                .attr('d', value => this.line(value))
                .attr('fill', 'none')
                .attr('stroke', 'purple');
    }

}

export default LineChart;

I was wondering whether it is possible to somehow call the updateChart() function in 'ChartWrapper.js' from say 'App.js' file since I would like to have the ability to update the chart values on the go (not render the whole page again)?

Say you get live values from a server and need to update the chart realtime, what would be the best way to create a flexible chart component?

Upvotes: 1

Views: 67

Answers (1)

OPearl
OPearl

Reputation: 399

You can define both the state hook and updateChart in App.js, and then pass them down to ChartWrapper.js as props:

// App.js

import React, { useState } from 'react';
import './App.css';

import ChartWrapper from './ChartWrapper'

const App = () => {
  const [data, setData] = useState([10, 13, 9.5, 11, 12, 14, 8, 13, 15, 14]);
  function updateChart() {
    setData(data.map(value => value + 5));
  }

  return (
    <div id="app">
      <ChartWrapper data={data} updateChart={() => updateChart()}/>
    </div>
  )
}

export default App;
// ChartWrapper.js

import React, { useEffect, useRef } from 'react';
import LineChart from './LineChart'

function ChartWrapper({ data, updateChart })  {

    const svgRef = useRef();
    //let data = [10, 13, 9.5, 11, 12, 14, 8, 13, 15, 14];
    console.log("works");

    useEffect( () => {
        const chart = new LineChart(svgRef, data);
        console.log(data);
    }, [data]);

    return (
        <div>
            <svg ref={svgRef} height = {300} width = {500}></svg>
        <button onClick={() => updateChart()}>Update Data</button>
        </div>

    );
}

export default ChartWrapper;

Also note that you should not use .push on the data array as you're doing in updateChart at the moment, but rather use setData like you're doing in the onClick callback.

Hope this helps. Good luck!

Upvotes: 2

Related Questions