Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Image onLoad event in isomorphic/universal react - register event after image is loaded

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

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

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} />
    );
}
like image 876
Everettss Avatar asked Sep 29 '16 18:09

Everettss


3 Answers

You could check the complete property on the image before applying the onload event.

if (!img.complete) {
    // add onload listener here
}
like image 74
Soviut Avatar answered Oct 22 '22 05:10

Soviut


This is all a bit tidier with Hooks:


const useImageLoaded = () => {
  const [loaded, setLoaded] = useState(false)
  const ref = useRef()

  const onLoad = () => {
    setLoaded(true)
  }

  useEffect(() => {
    if (ref.current && ref.current.complete) {
      onLoad()
    }
  })

  return [ref, loaded, onLoad]
}

const SomeComponent = ({ src }) => {
  const [ref, loaded, onLoad] = useImageLoaded()

  return (
    <div>
      <img ref={ref} onLoad={onLoad} src={src} alt="" />
      {loaded && <h1>Loaded!</h1>}
    </div>
  )
}
like image 18
coreyward Avatar answered Oct 22 '22 06:10

coreyward


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}
/>
like image 11
Idan Gozlan Avatar answered Oct 22 '22 04:10

Idan Gozlan