Skip to content

DOM element provided is null or undefined #199

@lavor

Description

@lavor

Hi, I am still getting this error in some circumstances.

[email protected]
[email protected]

I reported this error in the closed issue #52 two months ago. But I did not get any reaction, so I am opening a new issue.

I tried to debug my situation and come to finding that library is not checking if a component is mounted in promise handlers in updatePlotly function.

Short story
Hence, whenever the component mounts and in next moment it unmounts (before all promises in updatePlotly resolves), the syncWindowResize will call Plotly.Plots.resize with null (this.el) as an argument.

What happened in my situation in details (I've put some checkpoints in comments in bellow snippet):

updatePlotly(shouldInvokeResizeHandler, figureCallbackFunction, shouldAttachUpdateEvents) {
// updatePlotly: checkpoint 1
      this.p = this.p
        .then(() => {
          if (!this.el) {
            let error;
            if (this.unmounting) {
              error = new Error('Component is unmounting');
              error.reason = 'unmounting';
            } else {
              error = new Error('Missing element reference');
            }
            throw error;
          }
// updatePlotly: checkpoint 2
          return Plotly.react(this.el, {
            data: this.props.data,
            layout: this.props.layout,
            config: this.props.config,
            frames: this.props.frames,
          });
        })
        .then(() => this.syncWindowResize(shouldInvokeResizeHandler)  // updatePlotly: checkpoint 3)
        .then(this.syncEventHandlers)
        .then(() => this.figureCallback(figureCallbackFunction))
        .then(shouldAttachUpdateEvents ? this.attachUpdateEvents : () => {})
        .catch(err => {
          if (err.reason === 'unmounting') {
            return;
          }
          console.error('Error while plotting:', err); // eslint-disable-line no-console
          if (this.props.onError) {
            this.props.onError(err);
          }
        });
  1. getRef sets ref correctly
  2. componentDidMount
  3. updatePlotly - updatePlotly: checkpoint 1
  4. updatePlotly - updatePlotly: checkpoint 2
  5. componentWillUnmount
  6. getRef unmounting sets ref to null (see https://reactjs.org/docs/refs-and-the-dom.html#callback-refs)
  7. updatePlotly - updatePlotly: checkpoint 3
  8. syncWindowResize calls Plotly.Plots.resize with argument this.el, which was set to null (step 6) and that results in Error while plotting: Error: DOM element provided is null or undefined.

Possible solution
Do not call things in promise then handler when unmounting:

// ...
 .then(() => {
  if(!this.unmounting) {
    return this.syncWindowResize(shouldInvokeResizeHandler))
  }
}
// ...

Alternative solution
Do not use this.unmountig at all and implement solution with cancelable promises:
https://reactjs.org/blog/2015/12/16/ismounted-antipattern.html

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions