Reactji - Pure

Example app for http://robots.thoughtbot.com/react-rendering-misconception

by Robloche

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.24/browser.js"></script>
<script src="https://npmcdn.com/[email protected]/dist/react-with-addons.js"></script>
<script src="https://npmcdn.com/[email protected]/dist/react-dom.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/2.0.3/fetch.min.js"></script>
<div class="container">
  <div id="app"></div>
  <div id="renders"></div>
</div>

SCSS

$emoji: #62a6ff;
$reactions: #ff6363;

.container {
  max-width: 630px; 
}

.area {
  fill-opacity: 0.8;
}

.area.emoji {
  fill: $emoji;
  stroke: $emoji;
}

.area.reactions {
  fill: $reactions;
  stroke: $reactions;
}

.tick > line,
.domain {
  stroke: #ccc;
  fill: none;
}

.tick > text {
  fill: #888;
}

.mouse-line {
  stroke: #333;
}

.legend {
  display: flex;
  justify-content: space-around;
  padding: 12px;
  width: 100%;
}

.key {
  display: flex;
  align-items: center;
  width: 25%;
}

.key span {
  display: inline-block;
  margin-right: 12px;
  height: 24px;
  width: 24px;
}

.emoji > span {
  background: transparentize($emoji, 0.2);
}

.reactions > span {
  background: transparentize($reactions, 0.2);
}

#renders {
  padding: 12px;
}

Babel + JSX

// An example avoiding re-rendering components too oftern in React.
// A part of http://robots.thoughtbot.com/react-rendering-misconception

const dataUrl = "https://s3.amazonaws.com/thoughtbot-images/react-rendering-misconception/emoji-vs-reactions.json";

const height = 400;
const width = document.getElementById("app").offsetWidth;
const padding = 40;
let renders = 0;
    
function trackRender() {
  renders += 1;
  document.getElementById("renders").innerText = `renders: ${renders}`;
}

function calculateScales(data) {
  const xScale = d3.scaleTime().
    domain([d3.min(data, d => d.x), d3.max(data, d => d.x)]).
    range([padding, width - padding]);
  const yScale = d3.scaleLinear().
    domain([d3.min(data, d => d.y), d3.max(data, d => d.y)]).
    range([height - padding, padding]);
    
   return { x: xScale, y: yScale };
}
    
function closestPoint(mouseLocation, data) {
  const interpolatedX = data.scales.x.invert(mouseLocation[0]);
  const index = d3.bisector(d => d.x).left(data.values, interpolatedX);

  const points = [
    {
      data: data.values[index],
      distance: data.values[index].x - interpolatedX,
    },
  ];

  if (data.values[index - 1]) {
    points.push({
      data: data.values[index - 1],
      distance: interpolatedX - data.values[index - 1].x,
    });
  }

  return _.sortBy(points, "distance")[0].data;
}

function fetchData() {
  return fetch(dataUrl)
    .then(r => r.json())
    .then(json => {
      const parsed = json.map(j => ({ ...j, date: new Date(j["date"])}));
      const sorted = _.sortBy(parsed, j => j["date"]);
      const data = {
        emoji: { values: [] },
        reactions: { values: [] },
      };

      sorted.forEach(entry => {
        const date = new Date(entry["date"]);
        data["emoji"]["values"].push({ x: date, y: entry["emoji"] });
        data["reactions"]["values"].push({ x: date, y: entry["reactions"] });
      });

      for (let key in data) {
        const scales =...