title: Can't perform a React state update on an unmounted component
body: <h2>Problem</h2>
  
  <p>I am writing an application in React and was unable to avoid a super common pitfall, which is calling <code>setState(...)</code> after <code>componentWillUnmount(...)</code>.</p>
  
  <p>I looked very carefully at my code and tried to put some guarding clauses in place, but the problem persisted and I am still observing the warning.</p>
  
  <p>Therefore, I've got two questions:</p>
  
  <ol>
  <li><strong>How do I figure out from the stack trace</strong>, which particular component and event handler or lifecycle hook is responsible for the rule violation?</li>
  <li>Well, how to fix the problem itself, because my code was written with this pitfall in mind and is already trying to prevent it, but some underlying component's still generating the warning.</li>
  </ol>
  
  <h2>Browser console</h2>
  
  <pre><code>Warning: Can't perform a React state update on an unmounted component.
  This is a no-op, but it indicates a memory leak in your application.
  To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount
  method.
      in TextLayerInternal (created by Context.Consumer)
      in TextLayer (created by PageInternal) index.js:1446
  d/console[e]
  index.js:1446
  warningWithoutStack
  react-dom.development.js:520
  warnAboutUpdateOnUnmounted
  react-dom.development.js:18238
  scheduleWork
  react-dom.development.js:19684
  enqueueSetState
  react-dom.development.js:12936
  ./node_modules/react/cjs/react.development.js/Component.prototype.setState
  react.development.js:356
  _callee$
  TextLayer.js:97
  tryCatch
  runtime.js:63
  invoke
  runtime.js:282
  defineIteratorMethods/&lt;/prototype[method]
  runtime.js:116
  asyncGeneratorStep
  asyncToGenerator.js:3
  _throw
  asyncToGenerator.js:29
  </code></pre>
  
  <p><a href="https://i.stack.imgur.com/ATfMj.png" rel="noreferrer"><img src="https://i.stack.imgur.com/ATfMj.png" alt="enter image description here"></a></p>
  
  <h2>Code</h2>
  
  <p><strong>Book.tsx</strong></p>
  
  <pre><code>import { throttle } from 'lodash';
  import * as React from 'react';
  import { AutoWidthPdf } from '../shared/AutoWidthPdf';
  import BookCommandPanel from '../shared/BookCommandPanel';
  import BookTextPath from '../static/pdf/sde.pdf';
  import './Book.css';
  
  const DEFAULT_WIDTH = 140;
  
  class Book extends React.Component {
    setDivSizeThrottleable: () =&gt; void;
    pdfWrapper: HTMLDivElement | null = null;
    isComponentMounted: boolean = false;
    state = {
      hidden: true,
      pdfWidth: DEFAULT_WIDTH,
    };
  
    constructor(props: any) {
      super(props);
      this.setDivSizeThrottleable = throttle(
        () =&gt; {
          if (this.isComponentMounted) {
            this.setState({
              pdfWidth: this.pdfWrapper!.getBoundingClientRect().width - 5,
            });
          }
        },
        500,
      );
    }
  
    componentDidMount = () =&gt; {
      this.isComponentMounted = true;
      this.setDivSizeThrottleable();
      window.addEventListener("resize", this.setDivSizeThrottleable);
    };
  
    componentWillUnmount = () =&gt; {
      this.isComponentMounted = false;
      window.removeEventListener("resize", this.setDivSizeThrottleable);
    };
  
    render = () =&gt; (
      &lt;div className="Book"&gt;
        { this.state.hidden &amp;&amp; &lt;div className="Book__LoadNotification centered"&gt;Book is being loaded...&lt;/div&gt; }
  
        &lt;div className={this.getPdfContentContainerClassName()}&gt;
          &lt;BookCommandPanel
            bookTextPath={BookTextPath}
            /&gt;
  
          &lt;div className="Book__PdfContent" ref={ref =&gt; this.pdfWrapper = ref}&gt;
            &lt;AutoWidthPdf
              file={BookTextPath}
              width={this.state.pdfWidth}
              onLoadSuccess={(_: any) =&gt; this.onDocumentComplete()}
              /&gt;
          &lt;/div&gt;
  
          &lt;BookCommandPanel
            bookTextPath={BookTextPath}
            /&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    );
  
    getPdfContentContainerClassName = () =&gt; this.state.hidden ? 'hidden' : '';
  
    onDocumentComplete = () =&gt; {
      try {
        this.setState({ hidden: false });
        this.setDivSizeThrottleable();
      } catch (caughtError) {
        console.warn({ caughtError });
      }
    };
  }
  
  export default Book;
  </code></pre>
  
  <p><strong>AutoWidthPdf.tsx</strong></p>
  
  <pre><code>import * as React from 'react';
  import { Document, Page, pdfjs } from 'react-pdf';
  
  pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
  
  interface IProps {
    file: string;
    width: number;
    onLoadSuccess: (pdf: any) =&gt; void;
  }
  export class AutoWidthPdf extends React.Component&lt;IProps&gt; {
    render = () =&gt; (
      &lt;Document
        file={this.props.file}
        onLoadSuccess={(_: any) =&gt; this.props.onLoadSuccess(_)}
        &gt;
        &lt;Page
          pageNumber={1}
          width={this.props.width}
          /&gt;
      &lt;/Document&gt;
    );
  }
  </code></pre>
  
  <hr>
  
  <h1>Update 1: Cancel throttleable function (still no luck)</h1>
  
  <pre><code>const DEFAULT_WIDTH = 140;
  
  class Book extends React.Component {
    setDivSizeThrottleable: ((() =&gt; void) &amp; Cancelable) | undefined;
    pdfWrapper: HTMLDivElement | null = null;
    state = {
      hidden: true,
      pdfWidth: DEFAULT_WIDTH,
    };
  
    componentDidMount = () =&gt; {
      this.setDivSizeThrottleable = throttle(
        () =&gt; {
          this.setState({
            pdfWidth: this.pdfWrapper!.getBoundingClientRect().width - 5,
          });
        },
        500,
      );
  
      this.setDivSizeThrottleable();
      window.addEventListener("resize", this.setDivSizeThrottleable);
    };
  
    componentWillUnmount = () =&gt; {
      window.removeEventListener("resize", this.setDivSizeThrottleable!);
      this.setDivSizeThrottleable!.cancel();
      this.setDivSizeThrottleable = undefined;
    };
  
    render = () =&gt; (
      &lt;div className="Book"&gt;
        { this.state.hidden &amp;&amp; &lt;div className="Book__LoadNotification centered"&gt;Book is being loaded...&lt;/div&gt; }
  
        &lt;div className={this.getPdfContentContainerClassName()}&gt;
          &lt;BookCommandPanel
            BookTextPath={BookTextPath}
            /&gt;
  
          &lt;div className="Book__PdfContent" ref={ref =&gt; this.pdfWrapper = ref}&gt;
            &lt;AutoWidthPdf
              file={BookTextPath}
              width={this.state.pdfWidth}
              onLoadSuccess={(_: any) =&gt; this.onDocumentComplete()}
              /&gt;
          &lt;/div&gt;
  
          &lt;BookCommandPanel
            BookTextPath={BookTextPath}
            /&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    );
  
    getPdfContentContainerClassName = () =&gt; this.state.hidden ? 'hidden' : '';
  
    onDocumentComplete = () =&gt; {
      try {
        this.setState({ hidden: false });
        this.setDivSizeThrottleable!();
      } catch (caughtError) {
        console.warn({ caughtError });
      }
    };
  }
  
  export default Book;
  </code></pre>