问题
In isomorphic rendered page image can be downloaded before main script.js file. So image can be already loaded before react register onLoad event - never trigger this event.
script.js
constructor(props) {
super(props);
this.handleImageLoaded = this.handleImageLoaded.bind(this);
}
handleImageLoaded() {
console.log('image loaded');
}
render() {
return (
<img src='image.jpg' onLoad={this.handleImageLoaded} />
);
}
Scenario 1 - image.jpg is bigger than script.js
In this scenario everything is working fine. Event is registered before image is finally loaded so in console is image loaded message.
Scenario 2 - image.jpg is smaller than script.js
This scenario you can see problem described at the beginning of post. onLoad event is not triggered.
Question
What can I do in order to trigger onLoad event in scenario 2?
EDIT: Soviut answer implementation
To detect if image is ready on render you should check complete property on pure javascript img object:
constructor(props) {
super(props);
this.state = { loaded: false };
this.handleImageLoaded = this.handleImageLoaded.bind(this);
this.image = React.createRef();
}
componentDidMount() {
const img = this.image.current;
if (img && img.complete) {
this.handleImageLoaded();
}
}
handleImageLoaded() {
if (!this.state.loaded) {
console.log('image loaded');
this.setState({ loaded: true });
}
}
render() {
return (
<img src='image.jpg' ref={this.image} onLoad={this.handleImageLoaded} />
);
}
回答1:
You could check the complete property on the image before applying the onload event.
if (!img.complete) {
// add onload listener here
}
回答2:
Another way is to use ref and cover those both scenarios:
<img
ref={(input) => {
// onLoad replacement for SSR
if (!input) { return; }
const img = input;
const updateFunc = () => {
this.setState({ loaded: true });
};
img.onload = updateFunc;
if (img.complete) {
updateFunc();
}
}}
src={imgSrc}
alt={imgAlt}
/>
回答3:
img.complete is true even when the src load fails.
complete - Returns a Boolean that is true if the browser has finished fetching the image, whether successful or not. It also shows true, if the image has no src value.
- Use naturalWidth as determinant whether the img load was successfull or not
state = {
isLoading: true,
hasError: false,
}
myRef = React.createRef();
componentDidMount() {
const img = this.myRef.current;
if (img && img.complete) {
if (img.naturalWidth === 0) {
this.handleOnError();
} else {
this.handleImageLoaded();
}
}
}
handleImageLoaded = () => {
if (this.state.isLoading) {
this.setState({ isLoading: false });
}
}
handleOnError = () => {
this.setState({ hasError: true });
}
render() {
return (
<img
src={src}
alt={alt}
ref={this.myRef}
onError={this.handleOnError}
onLoad={this.handleOnLoad}
/>
);
}
- Another way is to add "check" image in componentDidMount a set eventHandler to it and let the check image be the one to handle eventListeners
componentDidMount() {
const testImg = new Image();
testImg.onerror = this.handleOnError;
testImg.onload = this.handleImageLoaded;
testImg.src = this.props.src; // important to set eventlisteners before src
}
回答4:
<img
src={this.props.imageUrl}
onLoad={this.handleImageLoaded.bind(this)}
onError={this.handleImageErrored.bind(this)}
/>
来源:https://stackoverflow.com/questions/39777833/image-onload-event-in-isomorphic-universal-react-register-event-after-image-is