Skip to content
Student Login

Christmas trees sold in USA

Last updated: December 2018Ā šŸ‘‰ livestreamed every last Sunday of the month. Join live or subscribe by email šŸ’Œ

Every year americans buy a bunch of christmas trees. Use the dataset to compare real and fake sales with two bar charts.

Dataset: Download dataset šŸ—³

My solution šŸ‘‡

How it works āš™ļø

Christmas trees sold in USA is an example of a simple barchart built with React and D3. More React than D3 really. šŸ˜‡

We used the simplified full integration approach because the data never changes. Neither do any of the other props like width and height. Means we don't have to worry about updating D3's internal state. Plopping D3 stuff into class field properties works great.

We converted our dataset from xlsx into a tab separated values file. Easy copy paste job with this tiny dataset. That goes in /public/data.tsv.

To load the dataset we use d3.tsv inside componentDidMount.

componentDidMount() {
d3.tsv("/data.tsv", ({ year, real_trees, fake_trees }) => ({
year: Number(year),
real_trees: Number(real_trees),
fake_trees: Number(fake_trees)
})).then(data => this.setState({ data }));
}

When the <App> component first loads, it makes a fetch() request for our data file. D3 parses the file as a list of tab separated values and passes each line through our data parsing function. That turns it into a nicely formatted object with real numbers.

.then we update component state with our data.

Inside the render method we use a conditional. When data is present, we render a <Barchart> component with a title. When there's no data, we render nothing.

No need for a loading screen with a dataset this small. Loads and parses super fast. šŸ‘Œ

Render the emoji barchart

Switchable emoji christmas tree barchart

We created a <Barchart> component that takes:

  • data, our entire dataset
  • value, the key name we're displaying
  • y, the vertical position

Final version doesn't need that vertical positioning param, but it's nice to have. You never know.

The Barchart uses a horizontal scaleBand to handle each column's positioning. Scale bands are a type of ordinal scale. They automatically handle spacing, padding, and making sure our columns neatly fit into a given width.

There's no height axis because we want each christmas tree emoji šŸŽ„ to represent a million real life trees.

We loop over the data and render a TreeBar and a text label for each entry.

<g transform={`translate(0, ${y})`}>
{data.map(d => (
<React.Fragment>
<TreeBar x={this.xScale(d.year)} y={480} count={d[this.props.value]} />
<text
x={this.xScale(d.year)}
y={500}
style={{ strike: 'black', fontSize: '12px' }}
textAnchor="center"
>
{d.year}
</text>
</React.Fragment>
))}
</g>

A grouping element holds everything in place and creates a common coordinate system to work off of. It's like a div. We position it with a transform=translate and elements inside position relatively to the group.

For each iteration of our data, we render a <TreeBar> and a <text>. Text goes at the bottom and displays the year. That's our label.

<TreeBar> gets a horizontal position through our xScale, a vertical position which is static, and a count of how many trees šŸŽ„ to show. We use this.props.value to dynamically fetch the correct part of each data object.

A <TreeBar> of emojis šŸŽ„

Now here's a silly little fun part: Instead of an SVG rectangle, we build each bar from a bunch of emoji text elements.

const TreeBar = ({ x, y, count }) => (
<g transform={`translate(${x}, ${y})`}>
{d3.range(count).map(i => (
<text x={0} y={-i * 12} style={{ fontSize: '20px' }}>
šŸŽ„
</text>
))}
<text
x={0}
y={-(count + 1) * 12 - 5}
style={{ fontSize: '9px' }}
textAnchor="center"
>
{count}
</text>
</g>
)

Once more we start with a grouping element. That holds everything together.

Then we create a fake array with d3.range and iterate. For each index in the array, we return a text element positioned at a 12px offset from the previous element, a fontSize of 20px, and a christmas tree emoji for content.

We found the values through experiment. Keeping emojis spaced to their full height created bars that were too hard to read. An overlap works great. Keeps the bars readable and the emojis recognizable.

That final little text shows how many million trees we drew in each bar. Makes our chart easier to read than counting tres :)

What you learned šŸ§

Today you learned:

  • D3 band scales
  • D3 range for creating iteration arrays
  • using class field values for D3 objects
  • a simple full integration approach

React for rendering. D3 to help calculate props.

āœŒļø

About the Author

Hi, Iā€™m Swizec Teller. I help coders become software engineers.

Story time šŸ‘‡

React+D3 started as a bet in April 2015. A friend wanted to learn React and challenged me to publish a book. A month later React+D3 launched with 79 pages of hard earned knowledge.

In April 2016 it became React+D3 ES6. 117 pages and growing beyond a single big project it was a huge success. I kept going, started live streaming, and publishing videos on YouTube.

In 2017, after 10 months of work, React + D3v4 became the best book I'd ever written. At 249 pages, many examples, and code to play with it was designed like a step-by-step course. But I felt something was missing.

So in late 2018 I rebuilt the entire thing as React for Data Visualization ā€” a proper video course. Designed for busy people with real lives like you. Over 8 hours of video material, split into chunks no longer than 5 minutes, a bunch of new chapters, and techniques I discovered along the way.

React for Data Visualization is the best way to learn how to build scalable dataviz components your whole team can understand.

Some of my work has been featured in šŸ‘‡

Created bySwizecwith ā¤ļø