Bring Data to Life on Mobile: D3.js & React Native

avatar
Marshal Murphy
April 17, 2020 - 6 MIN READ

D3 is pretty wild.

It allows us to create essentially any sort of chart or graph we can thing of.

We are able to visualize data using Javascript in any number of ways.

Check out the D3 sample library or head over to D3js.org and click on any of the hexagons in the banner to see just how wide-ranging the results can be.

In D3, we write functions that take in a dataset and dynamically build an SVG. Everything from generating the X- and Y- (and Z-) axes, to transforming the data to be bound within them and to plotting your data.

We are able to add onClick functions that transform the data in various ways, apply animations to these transforms, and even allow for new data to be introduced over time.

Naturally, this power comes at the price of a steep learning curve.😧

Feature

I recently set out to create a handful of graphs; two different types of bar graphs, and a scatter plot. I had always wanted to learn D3, and since this was a personal side project, I decided that now was a great time to take that dive.

My issue was that I could find very little in the way of tutorials online for creating D3 graphs in React Native. It wasn’t until I found this article by Harry Wolff from 2016 that I was able to get a grasp on how to do this.

The solution?

Use D3 in tandem with React Native ART, a library that renders SVG shapes and paths.

With D3 we can use a variety of methods that will generate the axes and transform our data to be bound within these axes. We will then use ART to generate shapes based on this data and draw them on the svg.

This is going to be a complex, very deep dive. So, let’s just jump in and see where it takes us.

Here’s the Final Project Repo if you’re in a hurry.

In-Place String Reversals


React Native Starter

Since we are building this in React Native, let’s quickly launch a boilerplate app using Expo.

Follow the steps I outlined in the beginning of this article to get set up, and for the installation options just select blank a minimal app as clean as an empty canvas. Come back here once you have the app running on your mobile device and reached the point where is says Choose Your Own Adventure.

In the end, you should have the app up and running on your device:

Starter


Dependencies: D3 & Art

Cool. Shut down the app from your terminal with control + c and lets install our dependencies. For creating our X and Y axes, we will need the d3-scale module.

yarn add d3 d3-scale

And for generating our SVG shapes, let’s install React Native ART:

yarn add @react-native-community/art

Lastly, we will need to be able to format dates, so lets pull in the library Moment.js:

yarn add moment

Some base styles for dark-mode

So, we could jump right in and make this bar graph on an ugly white background, OR we could lay a little UI groundwork so that this baby is something you’d be proud to bring home to meet your parents.

Let’s add a few more dependencies, then quickly drop in a header and footer, a title and subtitle, and mock Dark Mode with a gradient. In your terminal, add the following dependencies:

yarn add @fortawesome/react-native-fontawesome @fortawesome/free-solid-svg-icons @fortawesome/fontawesome-svg-core react-native-svg expo-linear-gradient

We’re adding fontawesome so that we can use a hamburger icon, which relies on react-native-svg to be rendered. We are also adding expo linear gradient so that we can mock a Dark Mode.

Now rewrite the App.js file as follows:

And now your app should look like this:

Dark UI

Cool.

Now before we get into building this chart, let’s just breeze over a couple things we implemented here:

  • Applied a Linear Gradient background to simulate an app in Dark Mode.
  • Added a top bar that uses fontawesome for the hamburger icon.
  • Added a footer because, well, we can.
  • Added a header and subheader to fill up some space.

The code above isn’t necessary to building this graph. I just wanted to make something pretty. As such, you may want to poke around the code a bit and read some of the documentation for StatusBar , LinearGradient, and FontAwesome. That way you can drop these into your next project.

Ready? Let’s build this thing.


Setting Up Our Directories

We are going to be making a complex graph component with multiple subcomponents, namely for the xAxis, yAxis, and columns, respectively. Let’s take a few moments to create a new BarGraph directory and build the subcomponents as empty functional components.

Begin by creating a new directory called BarGraph and adding an index.js file to it. In this file let's build out an empty functional react component. Add the following:

Now import this file into App.js and add the new component where I left the commented space with the text *The Column Chart will live here (you can remove the comment):

Since this component is empty you won’t see any changes in your UI. That’s okay. Let’s move on to constructing the X-Axis.

In the BarGraph folder, create a new file called xAxis.js and add the following code to it:

Great! 2 files down, 2 to go!

Just like with the xAxis, add a file called yAxis and populate it with the following component:

And just like with the xAxis & yAxis, create one last file called Columns.js and populate with the following:

Finally, import the xAxis, yAxis, and Columns components into BarGraph/index.js and implement them like so. Also, lets include some variables to pass into the XAxis for width and height:

Again, these components are empty, and so you won’t see a change in the UI.

Pfew! That’s a lot of empty components! But it will be useful to split it out this way. Alright, onwards to the xAxis!


X Axis & D3 Scaleband

Let’s take a moment to talk about the xAxis.js, and specifically, scaleBand which we have imported from d3-scale here.

The main concept around constructing graphs with D3 is the idea of drawing an SVG. We need a way to take a set of data and determine its x,y position on the SVG. The way to do this is to use a scale function, like scaleBand.

For this specific use case, we want to plot the 7 days of the week, ending in todays date. We know that each day is no greater or less than any other day, aka will be evenly spaced out, and so we can use the scaleBand function to achieve this.

First, lets write a function that will generate an object containing 2 arrays for days and their corresponding dates:

Here we are looping 7 times, using moment to get and format each day and date, and pushing to their respective arrays. We return this as an object, which looks like this:

Let’s add this function to our Axis component, place these values inside an ART grouping, and map over them.

Okay let’s unpack some of this new code. After we generated our values for days and dates, we map over them in order to plot them on the graph. We are doing this for both days, and dates. The reason is because, if you look at the very top image in this tutorial, the day of the week is above the day number, and therefore they have a different Y position.

I’ve included a handful of attributes with Text, such as alignment, font, and fill. An important attribute to look at here is opacity. Basically I am saying “If this is the last item being mapped, set to opacity 1, else set to 0.6”. This is what gives us that highlighted effect on today’s date.

Now, if you save and/or reload the app, you’re UI should look like this:

X Axis no plotting

In the bottom left we have our X Axis values, but they are all stacked on top of each other. This is because we have not yet computed X positions for them. Lets write a function, using scaleband, that will take in the index, values being assessed, and width, and return the x position for each value individually as they are being mapped:

Above we are creating a scaleBand that uses a function called rangeRound to shorten the width of the scale. Then we apply each value to x.domain and return the position generated for this specific index. Now we will call this function for x to accurately plot our xAxis:

Your XAxis component should now be complete and look like this:

… And you will now see an XAxis in the UI, showing the past week of dates, highlighting today:

X Axis plotted

Wow. That was a lot of work. And we have only covered the X Axis! Maybe I should have split this article into multiple parts. 🤔

Oh well, is what it is. Let’s move on to creating the YAxis and the gridlines.


Y Axis & D3 ScaleLinear

In BarGraph/index.js, add a variable for Y Axis height and pass it to the component along with width:

Then be sure to deconstruct these Props and use them in the Y Axis. We are also going to jump right into our first function here, generateGridPoints, as well as assign some arbitrary range of yValues:

To generate our grid, we need a number of points equal to the length of the yValues array, being 8 in this case. Our function generateGridPoints creates a range of values from 0 - 100 to represent this spread of 8 points on the graph.

With these values at our disposal, we are going to map over them and create our first Shape! Spoiler, its a horizontal line.

To generate the position of the gridlines, we create a function I’m calling getScaleTicks, which uses the D3-Scale method scaleLinear . ScaleLinear constructs a continuous scale with a specified domain and range. We have already computed our gridpoints with a scale from 0 - 100, and so we do the same with scaleLinear, establishing a domain of [0, 100] (values) and a range of [0, height - 10].

Said another way, the domain computes the values to be within a spread of 0–100, and the range plots these positions on the SVG according to the x and y values provided.

With these positions established, we can draw the gridlines using Path, moveTo, and line.

Gridlines

Lastly, we need to plot the yValues to establish our Y Axis. We will do this within the same mapping of the gridpoints, creating a new group and passing a new function getScalePosition to generate the y positions.

The completed YAxis component should look like so:

… and the UI will update to show all of our hard work:

Gridlines

We’re getting to the homestretch now. Let’s fight the pain and push on through!


FINALLY Drawing the Columns

Okay. Up to this point, we have explored ScaleBand, ScaleLinear, and drawing a shape using Path.

With these tools, we can finally plot some rectangles on this crazy thing.

Open up App.js and add the following variables for data and color and pass them to <BarGraph />.

Then, in BarGraph/index.js, pass them right on to <Columns /> and add a variable called barsHeight. The final BarGraph/index.js component will look like this:

Turning our attention to Columns.js, deconstruct the Props that we just passed in and pass width and height to our Surface. Our empty component will look as follows:

Now, our final function!

We need to map over the data we just passed in and somehow create rectangles. You may have noticed that we are also attaching some color to the tops of the rectangles. That means 2 different rectangles per data entry.

Inside Surface, let’s map our data and create a config variable to hold some of our values.

Then below the Columns function, create new functions called ConstructShape, _createX, and _createY.

Let’s first take a peak at _createX, and _createY.

We are using the same principles for the XAxis and YAxis components to create the x and y coordinates for each rectangle. Then in ConstructShape we use Path again to draw the rectangles.

Go ahead and play around with the values to see how things are effected.

Ultimately, with the d attribute constructed, we return our shape.

This brings us to our final product:

Final


Conclusion

I have to be honest with you: this tutorial turned out being WAY, WAY longer than I anticipated.

As such I apologize for not diving into deeper detail regarding D3 + ART mechanics. Largely I have written instructions as “here’s some code, check it out on your own time, here’s some more”.

That being said, it should give you a solid basis for how use D3 in React Native with React Native ART.

I love D3. In future articles, we will be exploring the mechanics in far more detail.

As always, if any of this didn’t work for you, or you just want to pull down the final product to explore, here is the Project Repo.

Great work! Now go build something awesome.

Marshal Murphy 2020