score:2

Accepted answer

You are heading in the right direction. When you need to use an external non react DOM library to render stuff in react, this is the way to go:

  1. Create a ref to a DOM element in the constructor.
  2. Start the plugins instance on componentDidMount(), and add reference to the plugin's instance(s) as properties on the component's instance. This will enable you to call the instance's methods from other methods.
  3. React to prop changes in componentDidUpdate(). Use the reference(s) to the plugin's instance to update it.
  4. In componentWillUnmount() clear everything the plugin added/scheduled/etc... such as event listeners, timeouts/intervals, DOM nodes created outside of the React tree, ongoing AJAX calls, etc...
  5. In render - don't add any properties to the container, so it won't be rerendered on props/state change.

Note: Before React 16.3 the standard way was to prevent rerender on props/state change by returning false in shouldComponentUpdate(), and reacting to props changes in componentWillReceiveProps(). However the latter is on it's way to being deprecated, and the former will be a recommendation instead of a strict order in the future.


This (non working) example is loosely based on the current VexFlow tutorial:

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>
    );
  }

}

Related Query

More Query from same tag