Sorun
React'te bir uygulama yazıyorum ve daha setState(...)sonra arayan süper yaygın bir tuzaktan kaçınamadım componentWillUnmount(...).
Koduma çok dikkatli baktım ve bazı koruma maddeleri koymaya çalıştım, ancak sorun devam etti ve hala uyarıyı gözlemliyorum.
Bu nedenle, iki sorum var:
- Yığın izlemeden , kural ihlalinden hangi bileşenin ve olay işleyicisinin veya yaşam döngüsü kancasının sorumlu olduğunu nasıl anlayabilirim ?
- Peki, sorunun kendisi nasıl çözülür, çünkü kodum bu tuzak düşünülerek yazılmıştı ve zaten onu önlemeye çalışıyor, ancak bazı temel bileşenler hala uyarıyı oluşturuyor.
Tarayıcı konsolu
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
Kod
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>
);
}
Güncelleme 1: Kısılabilir işlevi iptal edin (hala şans yok)
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;
