Den Dribbles

D3 Word Cloud with Create React App

May 10, 2019

Initialise CRA App

First of all, initalise the React app:

create-react-app d3-wordcloud && cd d3-wordcloud

Install D3 Deps

For this particular case, we want to install a particular set of D3 libs:

yarn add d3-scale d3-cloud d3-selection

Setting Up Our Data Stub

For simplicity sake, let’s create a JSON set of data that we can use to pass down into our final component.

In the src directory, let’s create data.json and add the following data:

{
  "data": [
    { "text": "frequency", "frequency": 63 },
    { "text": "function", "frequency": 61 },
    { "text": "data", "frequency": 47 },
    { "text": "return", "frequency": 36 },
    { "text": "attr", "frequency": 29 },
    { "text": "chart", "frequency": 28 },
    { "text": "array", "frequency": 24 },
    { "text": "style", "frequency": 24 },
    { "text": "layouts", "frequency": 22 },
    { "text": "values", "frequency": 22 },
    { "text": "need", "frequency": 21 },
    { "text": "nodes", "frequency": 21 },
    { "text": "pie", "frequency": 21 },
    { "text": "use", "frequency": 21 },
    { "text": "figure", "frequency": 20 },
    { "text": "circle", "frequency": 19 },
    { "text": "we'll", "frequency": 19 },
    { "text": "zoom", "frequency": 19 },
    { "text": "append", "frequency": 17 },
    { "text": "elements", "frequency": 17 }
  ]
}

We will import this JSON and pass it down to create the final file.

Updating App.js

Now, let’s update our App.js from it’s initial state to the following:

import React, { Component } from "react"
import logo from "./logo.svg"
import "./App.css"
import WordCloud from "./WordCloud"
import wordData from "./data.json"

class App extends Component {
  render() {
    return (
      <div className="App">
        <WordCloud
          id="wcloud"
          w="600"
          h="600"
          data={wordData.data}
          threshold={25}
        />
      </div>
    )
  }
}

export default App

There are some important steps to note from what we just did:

  1. We imported the data from data.json
  2. We imported our WordCloud component from the WordCloud.js file
  3. We add properties id, w, h, data and threshold - we will explore these properties a little bit later.

If you try running the app at the moment, it will evidently fail given we haven’t created the WordCloud.js file - so let’s do just that.

Creating the WordCloud

With the src folder, let’s create a WordCloud.js file.

Let’s fill out the WordCloud.js file with the following:

import React, { Component } from "react"
import { scaleLinear } from "d3-scale"
import cloud from "d3-cloud"
import { select } from "d3-selection"

/**
 * A D3 reusable Word Cloud
 *
 * @example
 * // sample usage with import
 * <WordCloud id="wcloud" w="600" h="600" data={wordData.data} threshold={25}/>
 *
 * @class WordCloud
 * @extends {Component}
 */
class WordCloud extends Component {
  componentDidMount() {
    this.drawChart()
  }

  drawChart() {
    const { id, w, h, data, threshold = 20 } = this.props

    const wordScale = scaleLinear()
      .domain([0, 75])
      .range([10, 120])

    const randomRotate = scaleLinear()
      .domain([0, 1])
      .range([-20, 20])

    cloud()
      .size([500, 500])
      .words(data)
      .rotate(() => randomRotate(Math.random()))
      .rotate(d => (d.text.length > 5 ? 0 : 90))
      .fontSize(d => wordScale(d.frequency))
      .on("end", draw)
      .start()

    function draw(words) {
      const svg = select("#" + id)
        .append("svg")
        .attr("height", h)
        .attr("width", w)
        .append("g")
        .attr("id", "wordCloud")
        .attr("transform", `translate(${w / 2},${h / 2})`)

      svg
        .selectAll("text")
        .data(words)
        .enter()
        .append("text")
        .style("font-size", d => d.size + "px")
        .style("fill", d => (d.frequency > threshold ? "#fe9922" : "#4f442b"))
        .attr("text-anchor", "middle")
        .attr("transform", d => `translate(${[d.x, d.y]})rotate(${d.rotate})`)
        .text(d => d.text)
    }
  }

  render() {
    return (
      <div
        id={this.props.id}
        style={{
          width: this.props.w,
          height: this.props.h,
        }}
      />
    )
  }
}

export default WordCloud

What you’ll note here is that our render function returns ONLY a div that will have a few properties to define the id and width and height style properties.

Secondly, we have an componentDidMount method that calls our drawChart function - this is important for appending our required SVG elements.

Thirdly, our drawChart function makes use of each of our props with a default value of 20 for threshold.

Our chart should now be up and looking pretty!

D3 WordCloud

Bam - our JSON data is defining the words required for the cloud and the colour is being determined by threshold parameter that we are using. Play around with this threshold to update the colour or remove it to use the default value of 20!

This is just a simple word colour. You could change the colour function or play around with it however you want from here.


A personal blog on all things of interest. Written by Dennis O'Keeffe, Follow me on Twitter