How to copy the code from an Observable in to a Create-react-app?

I’ve been working with react for over a year now, and have a good grasp of how to use it. I’m new to D3 and want to create a stock chart and have just discovered Observable. Specifically: https://beta.observablehq.com/@mbostock/d3-candlestick-chart

I have been reading “Data Visualization With D3 4.x”, and some good examples such as “https://medium.com/@Elijah_Meeks/interactive-applications-with-react-d3-f76f7b3ebc71”.

But I can’t get the code from Mike Bostock’s Observable demo to function in my react project. I have it all compiling with no errors, but nothing is rendered. How do all those code cells communicate with each other?

Can someone help me?

Hi Bryce,

Sure, there are two approaches to this challenge:

  1. You can port the code from Observable to vanilla JavaScript. To do this, you’ll want to learn about how Observable cells communicate with each other, which is extensively covered by the introductory notebook series.
  2. Or you can embed the notebook in React. @jashkenas has made a helpful example of how to do that here: https://beta.observablehq.com/@jashkenas/how-to-embed-a-notebook-in-a-react-app

Hope that helps! If you have any difficulties, keep in mind that a minimal, complete, verifiable example will help us a lot with debugging it and helping you.

-Tom

Thanks Tom. I copied all the cells to my React module. A copy is below. The props include an array of Amazon prices. It has been used before in another D3 chart without a problem.
When I run this, I do get some part of the chart (AMZN prices) as shown in this image…
image
But the rest of the D3 code is a mystery to me. That’s why I want to run this demo chart, so I can experiment with all the API’s and learn D3.

class ChartCandlesticks extends Component {

componentDidMount() {
this.createChart()
}

createChart() {
const { data, width, height } = this.props

const margin = { top: 20, right: 30, bottom: 30, left: 40 }

const x = d3
  .scaleBand()
  .domain(d3.timeDay.range(data[0].date, +data[data.length - 1].date + 1).filter((d) => d.getDay() !== 0 && d.getDay() !== 6))
  .range([margin.left, width - margin.right])
  .padding(0.2)

const y = d3
  .scaleLog()
  .domain([d3.min(data, (d) => d.low), d3.max(data, (d) => d.high)])
  .rangeRound([height - margin.bottom, margin.top])

const xAxis = (g) =>
  g
    .attr('transform', `translate(0,${height - margin.bottom})`)
    .call(
      d3
        .axisBottom(x)
        .tickValues(d3.timeMonday.every(width > 720 ? 1 : 2).range(data[0].date, data[data.length - 1].date))
        .tickFormat(d3.timeFormat('%-m/%-d'))
    )
    .call((g) => g.select('.domain').remove())

const yAxis = (g) =>
  g
    .attr('transform', `translate(${margin.left},0)`)
    .call(
      d3
        .axisLeft(y)
        .tickFormat(d3.format('$~f'))
        .tickValues(
          d3
            .scaleLinear()
            .domain(y.domain())
            .ticks()
        )
    )
    .call((g) =>
      g
        .selectAll('.tick line')
        .clone()
        .attr('stroke-opacity', 0.2)
        .attr('x2', width - margin.left - margin.right)
    )
    .call((g) => g.select('.domain').remove())

const formatDate = d3.timeFormat('%B %-d, %Y')

const formatValue = d3.format('.2f')

const parseDate = d3.timeParse('%Y-%m-%d')

const svg = d3
  .select(this.node)
  .append('svg')
  .attr('width', { width })
  .attr('height', { height })
  .append('g')
  .attr('transform', `translate( ${margin.left}, ${margin.top})`)

svg.append('g').call(xAxis)

svg.append('g').call(yAxis)

const g = svg
  .append('g')
  .attr('stroke-linecap', 'round')
  .attr('stroke', 'black')
  .selectAll('g')
  .data(data)
  .enter()
  .append('g')
  .attr('transform', (d) => `translate(${x(d.date)},0)`)
  .attr('stroke', (d) => (d.open > d.close ? d3.schemeSet1[0] : d.close > d.open ? d3.schemeSet1[2] : d3.schemeSet1[8]))

g.append('line')
  .attr('y1', (d) => y(d.low))
  .attr('y2', (d) => y(d.high))

g.append('line')
  .attr('y1', (d) => y(d.open))
  .attr('y2', (d) => y(d.close))
  .attr('stroke-width', x.bandwidth())

g.append('title').text(
  (d) => `${formatDate(d.date)}
      Open: ${formatValue(d.open)}
      Close: ${formatValue(d.close)}
      Low: ${formatValue(d.low)}
      High: ${formatValue(d.high)}`
)

}

render() {
return <div ref={(node) => (this.node = node)} />
}

}