Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3.js how to update chart when with new data

How to update chart with new data? I have a chart which displays price range between two dates. Then I added two datepickers to define dates . When I pick new dates and update chart it overlays new chart on old chart. How to delete old one and display chart with new data? I just started working with d3 and still didn't get how to update it

import React, { Component } from "react"
import * as d3 from "d3"
import DatePicker from "react-datepicker"
import "react-datepicker/dist/react-datepicker.css"
import moment from "moment"
import "./App.css"

class App extends Component {
  state = {
    data: null,
    startDate: new Date("2017-12-31"),
    endDate: new Date("2018-04-01"),
    isDateChanged: false,
  }

  async getData() {
    const { startDate, endDate } = this.state
    let startTime = moment(startDate).format("YYYY-MM-DD")
    let endTime = moment(endDate).format("YYYY-MM-DD")
    try {
      const response = await fetch(
        `https://api.coindesk.com/v1/bpi/historical/close.json?start=${startTime}&end=${endTime}`,
      )
      const json = await response.json()
      this.parseData(json)
    } catch (error) {
      console.log(error)
    }
  }
  handleChange(date, type) {
    if (type === "startDate") {
      this.setState({
        startDate: date,
      })
    } else {
      this.setState({
        endDate: date,
      })
    }
  }
  parseData(data) {
    var arr = []
    for (let i in data.bpi) {
      arr.push({
        date: new Date(i),
        price: data.bpi[i],
      })
    }
    this.setState({
      data: arr,
    })
  }

  displayChart(data) {
    let height = 400
    let width = 600

    let maxDate = d3.max(data, d => d.date)

    let minDate = d3.min(data, d => d.date)
    let maxPrice = d3.max(data, data => data.price)

    let y = d3
      .scaleLinear()
      .domain([0, maxPrice])
      .range([height, 0])
    let x = d3
      .scaleTime()
      .domain([minDate, maxDate])
      .range([0, width])

    let yAxe = d3.axisLeft(y)
    let xAxe = d3.axisBottom(x)

    let svg = d3
      .select("svg")
      .attr("height", "600")
      .attr("width", "800")

    let group = svg.append("g").attr("transform", "translate( 50 , 50)")
    let line = d3
      .line()
      .x(d => x(d.date))
      .y(d => y(d.price))

    group.append("path").attr("d", line(data))
    group
      .append("g")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxe)
    group.append("g").call(yAxe)
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevState.data !== this.state.data) {
      this.displayChart(this.state.data)
    }
    if (prevState.isDateChanged !== this.state.isDateChanged) {
      this.getData()
    }
  }
  componentDidMount() {
    this.getData()
  }

  render() {
    return (
      <div className="canvas">
        <div>
          <DatePicker
            selected={this.state.startDate}
            onChange={(date, startDate) => this.handleChange(date, "startDate")}
          />
          <DatePicker
            selected={this.state.endDate}
            onChange={(date, endDate) => this.handleChange(date, "endDate")}
          />
          <button onClick={() => this.setState({ isDateChanged: true })}>
            Enter
          </button>
        </div>
        <h1> Bitcoin price chart</h1>
        <svg className="line-chart" />
      </div>
    )
  }
}

export default App
like image 358
Yerlan Yeszhanov Avatar asked Sep 16 '25 16:09

Yerlan Yeszhanov


1 Answers

You can select all children of svg and remove them with the following line:

svg.selectAll("*").remove();

Stick that in your displayChart function:

displayChart(data) {

  ...

  let svg = d3
    .select("svg")
    .attr("height", "600")
    .attr("width", "800")

  svg.selectAll("*").remove();

  let group = svg.append("g").attr("transform", "translate( 50 , 50)")

  ...

}

d3.selectAll()

selection.remove()

like image 121
Danny Buonocore Avatar answered Sep 19 '25 05:09

Danny Buonocore