I'm having some trouble getting component lifecycles to cooperate with event listeners in React. Below is an example application that displays an image from a URL provided by a user and uses the width of the image to apply styling - in this case I want the image to appear at 1/2 scale so i get the width of the image and then give it a width of 1/2 it's actual width.
To do this I use the naturalWidth
of the image as a property in the render()
method. I create an instance of the image in the componentDidMount() method and then use an event listener to wait until the image has loaded to get its width. Without the event listener it will try to grab the width before the image is loaded and won't return a value so the listener ensures that the image is loaded before we get the width. That's where I'm running into a problem: The render()
method is not restricted by the event listener so even though componentDidMount()
is triggered before render()
, it always renders with the initial state. I was hoping that by changing the state as part of the event the component would re-render but that's not the case.
What is the "right" way to make sure the content renders AFTER the event listener has finished getting the value for the width of the image?
import React, {Component,} from 'react'
import './app.css'
class App extends Component {
// setting the initial state with a placeholder image
state = {
src: "https://cdn.jpegmini.com/user/images/slider_puffin_jpegmini_mobile.jpg",
width: "",
}
// Getting the width of the image when the component mounts
componentDidMount() {
let Img = document.createElement("img");
Img.src = this.state.src
Img.addEventListener("load", () => {
this.state.width = Img.naturalWidth
});
}
//re-calculating the width when the image changes (exact same as above)
componentDidUpdate() {
let Img = document.createElement("img");
Img.src = this.state.src
Img.addEventListener("load", () => {
this.state.width = Img.naturalWidth
});
}
// This function updates the state when the input is changed
change = (e) => {
this.setState({
[e.target.name]: e.target.value
})
};
render() {
return (
<div className="app">
<input
name="src"
value={this.state.src}
onChange={e => this.change(e)}
disabled={false}
></input>
<img
src={this.state.src}
style={{width: this.state.width / 2}}
></img>
</div>
)
}
}
export default App