Reputation: 10888
I'm in a bit of a pickle here, I've made myself a simple component that loads images asynchronously and displays them when ready. My issues is that I somehow am not removing the event listeners properly, because React complains that it cannot set state on an unmounted component.
My code:
export default class Image extends React.Component {
constructor() {
super()
this.state = {
preloadReady: false,
sourceReady: false,
img1: new window.Image(),
img2: new window.Image(),
}
}
load() {
let preload = this.refs.preloadElement,
src = this.refs.completeElement,
self = this, ctx1, ctx2, img1, img2, load;
ctx1 = preload.getContext('2d');
ctx2 = src.getContext('2d');
this.state.img1.addEventListener('load', () => {
load(this.state.img1, ctx1, preload)
this.setState({preloadReady: true});
})
this.state.img1.crossOrigin = "anonymous";
this.state.img1.src = this.props.preload;
this.state.img2.crossOrigin = "anonymous";
this.state.img2.addEventListener('load',
() => {
setTimeout( () => {load(this.state.img2, ctx2, src)
this.setState({sourceReady: true});
}, 100)
})
this.state.img2.src = this.props.src;
load = function(img, ctx, canvas) {
var data, filtered;
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img ,0 ,0 ,img.width,img.height,0,0, canvas.width, canvas.height);
}
}
componentDidMount() {
this.load();
}
componentWillUnmount() {
// The following does not work
this.state.img1.removeEventListener('load');
this.state.img2.removeEventListener('load');
}
render () {
let classes = {
src: '',
pre: ''
}
if (this.state.preloadReady) classes.pre = 'ready';
if (this.state.sourceReady) classes.src = 'ready';
return (
<div class="async-image tr_quick" style={{width: this.props.width, height: this.props.height}}>
<canvas ref="preloadElement" style={{width: this.props.width, height: this.props.height}} class={'preload tr_quick ' + classes.pre}></canvas>
<canvas ref="completeElement" style={{width: this.props.width, height: this.props.height}} class={'src tr_quick ' + classes.src}></canvas>
</div>
)
}
}
As you can see I'm trying to remove the listeners in componentWillUnmount
, but it says I am missing an argument?
Upvotes: 2
Views: 2724
Reputation: 5641
You shouldn't put your images into state
, instead define images like this:
constructor(props, context) {
super(props, context);
this.img1 = new window.Image();
this.img2 = new window.Image();
}
and instead adding event listener you can use the image's onload
event:
onImageLoaded(imgType) {
if (imgType === 'pre') {
...
}
...
}
this.img1.onload = this.onImageLoaded.bind(this, 'pre');
this.img2.onload = this.onImageLoaded.bind(this, 'real');
...
then remove the onload listener by assign null to it:
this.img1.onload = null;
and if you use some timers, always remove the timer in componentWillUnmount()
like:
// Create timer..
this.timer1 = setTimeout(() => {...}, 100);
componentWillUnmount() {
// Remove
clearTimeout(this.timer1);
}
Upvotes: 3