Averages Past the Whole in React
Using ApexCharts with React
This Averages Past the Whole example wires ApexCharts into React through the react-apexcharts component.
Install it with npm install react-apexcharts apexcharts, then mount the chart with <Chart type="..." options={options} series={series} />.
import React from 'react'
import ReactApexChart from 'react-apexcharts'
import ApexCharts from 'apexcharts'
import './styles.css'
import 'apexcharts/features/highlight-filter'
// Shared by the vanilla-js, React and Vue builds.
function money(v) {
return (v < 0 ? '-$' : '$') + Math.abs(Math.round(v))
}
//
// Average profit per order by store, across all products (the wholes), and the
// same average over one product line (the parts). An average over a subset
// can be larger than the overall one, or fall on the other side of zero, so
// the parts here do both.
var STORES = ['Seattle', 'Denver', 'Austin', 'Chicago', 'Boston', 'Miami']
var ALL = [42, -18, 31, 24, 12, -9]
var LINES = {
premium: [
{ x: 'Seattle', y: 42, highlight: 61 },
{ x: 'Denver', y: -18, highlight: -4 },
{ x: 'Austin', y: 31, highlight: 48 },
{ x: 'Chicago', y: 24, highlight: 19 },
{ x: 'Boston', y: 12, highlight: 27 },
{ x: 'Miami', y: -9, highlight: 6 },
],
budget: [
{ x: 'Seattle', y: 42, highlight: 18 },
{ x: 'Denver', y: -18, highlight: -26 },
{ x: 'Austin', y: 31, highlight: 12 },
{ x: 'Chicago', y: 24, highlight: 28 },
{ x: 'Boston', y: 12, highlight: -3 },
{ x: 'Miami', y: -9, highlight: -21 },
],
}
function seriesFor(line) {
return [
{
name: 'Avg profit per order',
data: line
? LINES[line]
: STORES.map(function (s, i) {
return { x: s, y: ALL[i] }
}),
},
]
}
function pickLine(chart, line) {
document.querySelectorAll('[data-line]').forEach(function (b) {
b.classList.toggle('active', b.getAttribute('data-line') === line)
})
return chart.updateSeries(seriesFor(line))
}
function wireLines(chart) {
document.querySelectorAll('[data-line]').forEach(function (b) {
b.addEventListener('click', function () {
pickLine(chart, b.getAttribute('data-line'))
})
})
}
const ApexChart = () => {
const [state, setState] = React.useState({
series: seriesFor('premium'),
options: {
chart: {
id: 'profit',
type: 'bar',
height: 360,
toolbar: { show: false },
},
colors: ['#0d9488'],
plotOptions: {
bar: { horizontal: true, barHeight: '62%', borderRadius: 4 },
},
dataLabels: {
enabled: true,
formatter: money,
},
xaxis: {
labels: { formatter: money },
},
grid: {
xaxis: { lines: { show: true } },
yaxis: { lines: { show: false } },
},
tooltip: {
y: { formatter: money },
},
},
})
React.useEffect(() => {
// The react-apexcharts wrapper owns the render, so reach the live instance
// by its chart.id. (seriesFor / pickLine live in the shared head script.)
const timer = window.setInterval(() => {
const chart = ApexCharts.getChartByID('profit')
if (!chart) return
window.clearInterval(timer)
wireLines(chart)
}, 50)
return () => window.clearInterval(timer)
}, [])
return (
<div>
<div className="wrap">
<h1>Averages can outgrow the whole</h1>
<p>
Each bar is a store's average profit per order across every product.
Pick a product line to draw that line's own average solid in front.
Where it beats the overall average, the axis stretches to fit it and a
dashed edge marks where the overall average ends; where it loses
money, it is drawn on its own side of zero.
</p>
<div className="actions">
<span>Product line</span>
<button data-line="premium" className="active">
Premium
</button>
<button data-line="budget">Budget</button>
<button data-line="">All (clear)</button>
</div>
<div className="chart-wrap">
<div id="chart">
<ReactApexChart
options={state.options}
series={state.series}
type="bar"
height={360}
/>
</div>
</div>
</div>
</div>
)
}
export default ApexChart