Can't perform a React state update on an unmounted component

Problem

I am writing an application in React and was unable to avoid a super common pitfall, which is calling setState(...) after componentWillUnmount(...).

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.

Therefore, I've got two questions:

  1. How do I figure out from the stack trace, which particular component and event handler or lifecycle hook is responsible for the rule violation?

  2. 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.

Browser console

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/</prototype[method]

runtime.js:116

asyncGeneratorStep

asyncToGenerator.js:3

_throw

asyncToGenerator.js:29

enter image description here

Code

Book.tsx

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: () => void;

pdfWrapper: HTMLDivElement | null = null;

isComponentMounted: boolean = false;

state = {

hidden: true,

pdfWidth: DEFAULT_WIDTH,

};

constructor(props: any) {

super(props);

this.setDivSizeThrottleable = throttle(

() => {

if (this.isComponentMounted) {

this.setState({

pdfWidth: this.pdfWrapper!.getBoundingClientRect().width - 5,

});

}

},

500,

);

}

componentDidMount = () => {

this.isComponentMounted = true;

this.setDivSizeThrottleable();

window.addEventListener("resize", this.setDivSizeThrottleable);

};

componentWillUnmount = () => {

this.isComponentMounted = false;

window.removeEventListener("resize", this.setDivSizeThrottleable);

};

render = () => (

<div className="Book">

{ this.state.hidden && <div className="Book__LoadNotification centered">Book is being loaded...</div> }

<div className={this.getPdfContentContainerClassName()}>

<BookCommandPanel

bookTextPath={BookTextPath}

/>

<div className="Book__PdfContent" ref={ref => this.pdfWrapper = ref}>

<AutoWidthPdf

file={BookTextPath}

width={this.state.pdfWidth}

onLoadSuccess={(_: any) => this.onDocumentComplete()}

/>

</div>

<BookCommandPanel

bookTextPath={BookTextPath}

/>

</div>

</div>

);

getPdfContentContainerClassName = () => this.state.hidden ? 'hidden' : '';

onDocumentComplete = () => {

try {

this.setState({ hidden: false });

this.setDivSizeThrottleable();

} catch (caughtError) {

console.warn({ caughtError });

}

};

}

export default Book;

AutoWidthPdf.tsx

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) => void;

}

export class AutoWidthPdf extends React.Component<IProps> {

render = () => (

<Document

file={this.props.file}

onLoadSuccess={(_: any) => this.props.onLoadSuccess(_)}

>

<Page

pageNumber={1}

width={this.props.width}

/>

</Document>

);

}


Update 1: Cancel throttleable function (still no luck)

const DEFAULT_WIDTH = 140;

class Book extends React.Component {

setDivSizeThrottleable: ((() => void) & Cancelable) | undefined;

pdfWrapper: HTMLDivElement | null = null;

state = {

hidden: true,

pdfWidth: DEFAULT_WIDTH,

};

componentDidMount = () => {

this.setDivSizeThrottleable = throttle(

() => {

this.setState({

pdfWidth: this.pdfWrapper!.getBoundingClientRect().width - 5,

});

},

500,

);

this.setDivSizeThrottleable();

window.addEventListener("resize", this.setDivSizeThrottleable);

};

componentWillUnmount = () => {

window.removeEventListener("resize", this.setDivSizeThrottleable!);

this.setDivSizeThrottleable!.cancel();

this.setDivSizeThrottleable = undefined;

};

render = () => (

<div className="Book">

{ this.state.hidden && <div className="Book__LoadNotification centered">Book is being loaded...</div> }

<div className={this.getPdfContentContainerClassName()}>

<BookCommandPanel

BookTextPath={BookTextPath}

/>

<div className="Book__PdfContent" ref={ref => this.pdfWrapper = ref}>

<AutoWidthPdf

file={BookTextPath}

width={this.state.pdfWidth}

onLoadSuccess={(_: any) => this.onDocumentComplete()}

/>

</div>

<BookCommandPanel

BookTextPath={BookTextPath}

/>

</div>

</div>

);

getPdfContentContainerClassName = () => this.state.hidden ? 'hidden' : '';

onDocumentComplete = () => {

try {

this.setState({ hidden: false });

this.setDivSizeThrottleable!();

} catch (caughtError) {

console.warn({ caughtError });

}

};

}

export default Book;

Answer

To remove - Can't perform a React state update on an unmounted component warning, use componentDidMount method under a condition and make false that condition on componentWillUnmount method. For example : -

class Home extends Component {

_isMounted = false;

constructor(props) {

super(props);

this.state = {

news: [],

};

}

componentDidMount() {

this._isMounted = true;

ajaxVar

.get('https://domain')

.then(result => {

if (this._isMounted) {

this.setState({

news: result.data.hits,

});

}

});

}

componentWillUnmount() {

this._isMounted = false;

}

render() {

...

}

}

以上是 Can't perform a React state update on an unmounted component 的全部内容, 来源链接: utcz.com/a/23673.html

回到顶部