Изучение того, может ли вам выгодно использовать библиотеку D3.js с проектом React.

Когда меня спросили о том, как создать пользовательскую графику в виде диаграммы в приложении React, моей первой реакцией было предложить библиотеку D3.js. Позже, однако, я задался вопросом, что предоставляет D3.js, а React - нет.

Предварительные требования

Во-первых, вам необходимо базовое понимание D3.js. Я собирался написать руководство по D3.js и вместо этого нашел отличный ресурс.



Во-вторых, вам необходимо базовое понимание React. Я написал руководство по React.



Повторное внедрение примера Enter, Update, Exit

В статье Enter, Update, Exit представлен простой D3.js пример, иллюстрирующий основные функции D3.js. Нажатие кнопки Добавить элемент создает полосы, а нажатие на полосы удаляет их.

Мы можем легко повторно реализовать это приложение с помощью React.

Давайте сравним решения D3.js и React. Оба решения предоставляют механизм для динамического построения шаблона элементов DOM; шаблон.

D3.js: ./js/main.js

...
selection.enter()
    .append("div").attr("class", "bar")
    .style("height", function(d){ 
      return d; 
    })
    .style("margin-top", function(d){ 
      return maxHeight - d; 
    })
    .on("click", function(e, i){
      dataset.splice(i, 1);
      update();
    });
...

React: ./react-introduction/src/Bar.js

...
const Bar = ({ height, onClick, position }) => (
  <div
    className="bar"
    style={{ height, marginTop: MAX_HEIGHT - height }}
    onClick={() => onClick(position)}
  />
);
...

Оба обеспечивают управление слушателем. При взаимодействии с веб-приложением и удалении элементов DOM необходимо также удалить все прикрепленные прослушиватели событий. В противном случае у вас будет утечка памяти.

В итоге основные функции D3.js доступны в React.

Реализация трех маленьких кругов

Предыдущий пример не был очень графическим; Итак, давайте рассмотрим повторную реализацию еще одного примера с использованием элементов масштабируемой векторной графики (SVG).

Основные функции приложения React следующие:

React: ./react-three-little-circles/src/App.js

...
const App = () => (
  <svg width="720" height="120">
    { DATA.map((o, i) => (
      <circle
        key={i}
        cx={(i * 100) + 30}
        cy="60"
        r={Math.sqrt(o)}
        style={{ fill: 'steelblue' }}
      />
    ))}
  </svg>
);
...

Поскольку элементы SVG - это просто элементы DOM, с ними легко реализовать решение React.

Переходы

Возможно, мы обнаружим, что переходы D3.js трудно повторно реализовать в React.

примечание. Если вы не знакомы с переходами D3.js, ознакомьтесь со статьей Работа с переходами.

Сначала мы добавляем следующие строки в пример Enter, Exit, Update, чтобы реализовать базовый цветовой переход.

D3.js: ./d3-introduction-transitions/js/main.js

...
.transition()
.style('background-color', 'red')
.duration(2000);
...

Одно интересное наблюдение заключается в том, что переход реализован в JavaScript, то есть без использования переходов или анимации CSS. Вы можете убедиться в этом, изучив элемент в Инструментах разработчика Chrome; наблюдая за изменением цвета фона.

Поскольку цветовые переходы легко реализовать в CSS, это довольно просто реализовать в React. Решение состоит в том, чтобы переключить класс, который запускает переход CSS после монтирования компонента Bar.

React: ./react-introduction-transitions/src/Bar.js

...
class Bar extends Component {
  constructor(props) {
    super(props);
    this.state = {
      active: false,
    };
  }
  componentDidMount() {
    window.setTimeout(() => {
      this.setState({
        active: true,
      });
    }, 0);
  }
  render() {
    const { height, onClick, position } = this.props;
    const { active } = this.state;
    return (
      <div
        className={`bar${active ? ' bar--active' : ''}`}
        style={{ height, marginTop: MAX_HEIGHT - height}}
        onClick={() => onClick(position)}
      />
    );
  }
}
...

Анимация SVG

Один из ключей к поиску примера, в котором мы могли бы использовать D3.js в нашем проекте React, можно найти в Руководстве по SVG-анимации (SMIL) .

SVG можно стилизовать и анимировать с помощью CSS (слайды). По сути, любое преобразование или анимация перехода, которая может быть применена к элементу HTML, также может быть применена к элементу SVG. Но есть некоторые свойства SVG, которые нельзя анимировать с помощью CSS, но которые можно анимировать с помощью SVG.

Сара Суэйдан - Уловки CSS

Между прочим, в статье также указывается, что стандарт SMIL умирает и что ответ - JavaScript.

Радиус SVG круга определяется атрибутом r; не CSS. Таким образом, это пример, когда использование CSS для перехода / анимации не будет работать для анимации радиуса круга SVG.

примечание: я не мог придумать другого простого способа CSS для имитации анимации радиуса круга SVG; играл с различными преобразованиями CSS.

Давайте посмотрим, как мы сделаем это в D3.js, анимировав наш пример Три маленьких кружочка.

Ключевой код добавлял следующее:

D3.js: d3-three-little-circle-transitions / js / main.js

...
.transition()
.attr('r', function(d) { return (2 * Math.sqrt(d)); })
.duration(2000);
...

Здесь у нас есть пример, в котором нам может потребоваться использовать D3.js в нашем проекте React.

React + D3.js

Чтобы проиллюстрировать совместное использование React и D3.js, мы реорганизуем нашу повторную реализацию React Enter, Update, Exit Example путем включения D3.js.

Сначала мы устанавливаем D3.js в наш проект React; выполнение следующего из корня приложения.

yarn add d3

Ключ состоит в том, чтобы обернуть код D3.js в компонент React и использовать жизненный цикл компонента React для запуска D3.js рендеринг.

React: ./react-d3-introduction/src/Chart.js

import React, { Component } from 'react';
import { PropTypes } from 'prop-types';
import { select } from "d3-selection";
import 'd3-transition';
import { MAX_HEIGHT } from './strings';
class Chart extends Component {
  constructor(props) {
    super(props);
    this.update = this.update.bind(this);
  }
  componentDidMount() {
    const { dataset } = this.props;
    this.update(dataset);
  }
  componentWillReceiveProps({ dataset }) {
    this.update(dataset);
  }
  shouldComponentUpdate() {
    return false;
  }
  update(dataset) {
    const { onClick } = this.props;
    const selection = select("#chart")
      .selectAll(".bar").data(dataset)
      .style("height", function(d){
        // HAvE TO USE PX WITH NPM VERSION
        return `${d.toString()}px`;
      })
      .style("margin-top", function(d){
        return `${(MAX_HEIGHT - d).toString()}px`;
      });
    selection.enter()
      .append("div").attr("class", "bar")
      .style("height", function(d){
        return `${d.toString()}px`;
      })
      .style("margin-top", function(d){
        return `${(MAX_HEIGHT - d).toString()}px`;
      })
      .on("click", function(_, i) {
        onClick(i);
      })
      .transition()
      .style('background-color', 'red')
      .duration(2000);
    selection.exit().remove();
  }
  render() {
    return (
      <div id="chart" />
    );
  }
}
Chart.propTypes = {
  dataset: PropTypes.array.isRequired,
  onClick: PropTypes.func.isRequired,
}
export default Chart;

примечание: хотя полная библиотека D3.js довольно велика, мы добавили только те функции, которые мы используем. В результате мы увеличили размер развернутого кода JavaScript с 158 КБ до 207 КБ (примерно 50 КБ).

Подведение итогов

Мы изучили примеры, в которых можно было бы использовать React для решения проблем, для решения которых мы могли обратиться к D3.js. Мы нашли образец примеров анимации атрибутов SVG, где D3.js имеет мощные функции, которые нелегко воспроизвести с помощью React. И, наконец, мы создали приложение React, которое включает D3.js для выполнения специального рендеринга.