Now that we’re done with the table, we can add the chart. The chart should show the bills that have been received in the last 30 days.

I like using the Chart.js charts library, and here’s a great React wrapper for it: https://github.com/jerairrest/react-chartjs-2

Add it as a dependency in CodeSandbox, and also add chart.js as a dependency.

You can also install those 2 libraries locally like we did before:

yarn add react-chartjs-2 chart.js

# or with npm:

npm install react-chartjs-2 chart.js

In src/index.js, let’s change

<Chart />

to

<Chart bills={bills} />

so the Chart component has all the things it needs to display bills (and also, when bills are updated, the chart is automatically refreshed)

Now we can focus on the src/components/Chart.js file.

Chart.js can make a wide variety of charts. We want a simple bar graph, and here’s a nice example using react-chartjs-2: https://github.com/jerairrest/react-chartjs-2/blob/master/example/src/components/bar.js

We import Bar from the library:

import { Bar } from 'react-chartjs-2';

we also import the Moment library, which we’ll use in a second:

import moment from 'moment'

Now we have some pure JavaScript calculations.

Every bar should show a month, and I want to determine what are the last 12 months, so the current line is the current month, and we have other 11 bars.

Here’s the function I came up with. months is the list of month names, and based on the current month, which we determine from the current date, I fill the orderedMonths array, and I return it.

const last12Months = () => {
  const months = [
    'January',
    'February',
    'March',
    'April',
    'May',
    'June',
    'July',
    'August',
    'September',
    'October',
    'November',
    'December'
  ]

  const today = new Date()
  const orderedMonths = []
  let month = today.getMonth() + 1

  for (let i = 0; i < 12; i++) {
    orderedMonths.push(months[month])
    month === 11 ? (month = 0) : month++
  }

  return orderedMonths
}

Hint: if the function seems complicated, try pasting it in the browser console, and call it using last12Months(), and try to change anything in the code until you’re comfortable with it. Adding a bunch of console.log() statements usually helps.

The next function, processBills, accepts an array of bills, filters out bills older than 1 year, and returns the total amount we paid each month:

const processBills = bills => {
  const oneYearAgo = moment().subtract(1, 'years')
  const months = last12Months()
  const monthsWithValues = new Array(12).fill(0)

  for (const month of monthsWithValues) {
    monthsWithValues[month] = 0
  }

  for (const bill of bills) {
    if (moment(bill.date).isSameOrBefore(oneYearAgo)) {
      continue
    }
    const monthName = moment(bill.date).format('MMMM')
    const indexOfMonth = months.indexOf(monthName)
    monthsWithValues[indexOfMonth] += parseInt(bill.amount)
  }

  return monthsWithValues
}

Again, this is just plain JavaScript, you can accept this function as working or inspect it as you wish.

Now we’re at the React component code. Here’s where we put all the things together: we use the last12Months() data for the labels, and the processBills() result for each month. We craft this in a data object as needed by the Bar component, and we feed that into it:

export default props => {
  const data = {
    labels: last12Months(),
    datasets: [
      {
        label: 'Amount',
        backgroundColor: 'rgba(255,99,132,0.2)',
        borderColor: 'rgba(255,99,132,1)',
        borderWidth: 1,
        hoverBackgroundColor: 'rgba(255,99,132,0.4)',
        hoverBorderColor: 'rgba(255,99,132,1)',
        data: processBills(props.bills)
      }
    ]
  }

  return (
    <div>
      <Bar
        data={data}
        width={100}
        height={550}
        options={{ maintainAspectRatio: false }}
      />
    </div>
  )
}

Since we pass bills as props, every time we add or remove a bill the chart automatically updates.

See the app in the current state on CodeSandbox.


Go to the next lesson