Using Victory Charts to Display Data Changes Over Time

Michael Bush
4 min readAug 14, 2018

--

As a new developer, incorporating a new library into a project inevitably involves a learning curve, and frequently uncovers additional challenges and learning opportunities that at first blush might seem orthogonal to the task at hand.

For our final project at Fullstack Academy in Chicago, my capstone team needed to incorporate a set of data visualizations into our satirical, gamified-stock-market app, and we set out to find an appropriate library to help .

After some initial experimentation with D3, we decided to try something that would play more nicely with React, and Formidable’s Victory Chart suite came highly recommended (it’s used by Nate Silver’s 538, and other major news organizations).

Victory’s library is mercifully easy to use, and very modular. And the docs are not too bad, albeit a little vague in some places. If you know React, it’ll look very familiar. Here’s how you make a basic line chart:

<VictoryChart data={basicData} height={250}>
<VictoryLine
interpolation=”linear”
data={basicData}
x={x} y={y}
style={{data: {stroke: ‘#c43a31’, strokeWidth: 1}}}
/>
</VictoryChart>

Here’s the basic data we’re passing it.

const basicData = [{x: 0, y: 2}, {x: 1, y: 2.5}, {x: 2, y: 4}, {x: 3, y: 5}, {x: 4, y: 3}, {x: 5, y: 1}, {x: 6, y: 3}]

And here’s what the line graph looks like:

Pretty cool! Now, if you want the individual nodes to be more visible, just add <VictoryScatter /> either before or after (or instead of!) <VictoryLine />. (Note: if <VictoryLine /> is ordered first, then the scatter nodes will render on top of the lines; otherwise the reverse is true.) Here are the two together:

And here’s the code for the scatter nodes, which, in this case came before <VictoryLine />:

<VictoryScatter 
style={{data: {fill: 'green'}}}
size={7}
data={basicData}
/>

OK, but what if you have lots of data points, and a chronological x-axis with lots of unevenly ordered dates, and you don’t want the date labels to overlap each other, and you want to be able to zoom in on the chart to an infinitely granular level, and you want to have the dates automagically parse themselves out of your way? Well, you can do it, and the Victory docs even make it seem easy. But there are a couple things they don’t tell you, that’ll leave your charts looking something like this:

Ouch. Well, first things first. After you’ve called all your data from the API, you’ll want to reformat it so that it’s an array of {x: date, y: price} objects. Victory will also allow you to specify this data like so:

<VictoryChart data={basicData} height={250}>
<VictoryLine
interpolation=”linear”
data={basicData}
x="basicData.date" y="basicData.price"
style={{data: {stroke: ‘#c43a31’, strokeWidth: 1}}}
/>
</VictoryChart>

Next, if you’re dealing with dates, you need to turn them into bona fide Javascript Date objects, using the new Date() constructor. Remember that this construction is lost when the data comes over from your API, since all you get is a bunch of JSON objects. In order for JS, and Victory, to recognize the dates for what they are, you’ll need to re-instantiate them. (Incidentally, if you’re unfamiliar with the JS Date object, here’s a good, thorough article about it ).

You’ll probably do this whole procedure in a somewhat unwieldy Redux Selector, way down at the bottom of your store. It’ll look something like this:

export const getSingleStockChart = (state, stockId) => {
return state.transactions.allIds.reduce((result, transId) => {
if (state.transactions.byId[transId].stockId == stockId)
result.push({
x: new Date(state.transactions.byId[transId].seedDate),
y: state.transactions.byId[transId].price
})
return result
}, [])
}

If the deeply nested object syntax frightens you, worry not, the only really important part is that you’re returning an array in which the date object is declared as a new instance of the JS Date object (i.e. new Date(“2018–08–13T17:34:28.779Z”)).

Once you’ve done that, Victory performs all kinds of magic!

Combine this approach with <VictoryZoomContainer />, a basic handleZoom() function and a sensible default state.zoomDomain, and you’re off to the races! Oh, one more thing: make sure you pass scale as props to your <VictoryChart/>, like so: scale={{x: ‘time’}}. That’s your cue to Victory to recognize your Date objects for what they truly are.

Hope that helps you avoid some of the pitfalls that I had. Once our project is live, I’ll try to come back and post the actual zoomable, properly dated charts. For now, check out the relevant Victory docs.

Thanks to the terrific people at Formidable for making such a rad library! Feel free to leave a comment if this helped you!

--

--