Сторонние манипуляции с DOM в React - PullRequest
0 голосов
/ 08 мая 2018

В настоящее время я занимаюсь портированием устаревшего приложения BackboneJS на ReactJS. Приложение использует VexFlow , движок рендеринга музыкальной нотации JavaScript. Одна из основных проблем, с которыми я столкнулся, заключается в том, что VexFlow отображает все само, SVG, аналогично D3. Существует много информации о комбинировании D3 с React, и я следую тому, что кажется обычной практикой в ​​случае использования пустого элемента React ref в качестве цели для моего рендеринга VexFlow, который все происходит в componentDidMount:

export default class ScoreComponent extends React.Component {

  constructor(props) {
    super(props);
    // Create a 'ref' object, which allows us to reference the React DOM
    // element we create in the render method.
    this.scoreElem = React.createRef();
    ...
  }

  componentDidMount() {
    var score = this.score;
    var elem = this.scoreElem.current;
    score.setElem(elem).render(); // <- All VexFlow rendering happens here...
    ...
  }

  render() {
    return (
      <div className="score" id={this.props.scoreId} ref={this.scoreElem}></div>
    );
  }

}

Хотя это работает, это делает меня довольно неудобным, особенно потому, что мне также необходимо добавить достаточное количество кода jQuery для обработки взаимодействия с пользователем по элементам SVG (например, нажатие и перетаскивание объектов сложных путей), ни один из которых React не будет быть в курсе.

Итак, мой вопрос: я иду по пути, который приведет к тому, что я сгорю? Мне очень нравится React, и я хочу попрощаться с Backbone. В выходные я смог безболезненно перенести большую часть своего кода пользовательского интерфейса. Я смотрел на Angular в прошлом, но он кажется слишком сложным и самоуверенным.

1 Ответ

0 голосов
/ 09 мая 2018

Вы движетесь в правильном направлении. Когда вам нужно использовать внешнюю не реагирующую DOM-библиотеку для рендеринга вещей в реагировать, вот путь:

  1. Создать ссылку на элемент DOM в конструкторе.
  2. Запустите экземпляр плагина на componentDidMount() и добавьте ссылку на экземпляр (-ы) плагина в качестве свойств на экземпляре компонента. Это позволит вам вызывать методы экземпляра из других методов.
  3. Реагировать на изменения в componentDidUpdate(). Используйте ссылку (и) на экземпляр плагина, чтобы обновить его.
  4. В componentWillUnmount() очистить все, что плагин добавил / запланировал / и т. Д. ..., например, прослушиватели событий, тайм-ауты / интервалы, узлы DOM, созданные вне дерева React, текущие вызовы AJAX и т. Д. .
  5. При рендеринге - не добавляйте какие-либо свойства в контейнер, поэтому он не будет перерисован при изменении реквизита / состояния.

Примечание: До Реакции 16.3 стандартным способом было предотвратить повторное рендеринг при изменении реквизита / состояния путем возврата false в shouldComponentUpdate() и реагирования на изменения реквизита в componentWillReceiveProps(). Однако последний находится на пути к устареванию, и в будущем он станет рекомендацией, а не строгим порядком.


Этот (нерабочий) пример слабо основан на текущем учебнике VexFlow :

export default class ScoreComponent extends React.Component {
  constructor(props) {
    super(props);
    // 1. create a ref to a DOM element
    this.scoreElem = React.createRef();
    ...
  }

  componentDidMount() {
    const { size } = this.props;
    const elem = this.scoreElem.current;
    // 2. add a reference to the plugin's instance, so you   
    //    can call the plugin in other lifecycle methods
    this.renderer = new VF.Renderer(elem, VF.Renderer.Backends.SVG)
    renderer.resize(size.w, size.h);
    this.context = renderer.getContext();
    ...
  }  

  componentDidUpdate (prevProps) {
    // 3. if the props effect the plugin
    // do something with the plugin
    // for example:
    const { size } = this.props;
    if(size !== prevProps.size) this.renderer.resize(size.w, size.h);
  }

  componentWillUnmount() {
    // 4. teardown:
    // run VexFlow destroy method if available
    // remove non react event listeners
    // clear timeouts and intervals
    // remove DOM nodes rendered outside of react container
    // cancel ongoing AJAX calls
    // etc...
  }

  render() {
    // 5. use only ref on the returned element, any use of properties/state might rerender the element itself.
    return (
      <div className="score" ref={this.scoreElem}></div>
    );
  }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...