Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to detect when a image is loaded, that is provided via props, and change state in React?

I want to load a different image(fake avatar) while the final avatar image is loading. The idea is to detect when the prop image is loaded and change a state. Is it possible? Some ideas? Thank you!

class ImageUser extends React.Component {

constructor(props) {
    super(props);
    this.state = {userImageLoaded: false};
    let imageSrc = "";

    if (!this.props.userImage) {
        imageSrc = this.props.noUserImage;
    } else {
        imageSrc = this.props.userImage;
    }

    this.loadingImage = <img className={styles.imageUser}
                     src={this.props.loadingImage} alt="2"/>;

    this.userImage =
        <img onLoad={this.setState({userImageLoaded: true})}
             className={styles.imageUser} src={imageSrc}
             alt="1"/>;

}

render() {
    let image = "";
    if (this.state.userImageLoaded) {
        image = this.userImage;
    } else {
        image = this.loadingImage;
    }
    return (
        <div>
            {image}
        </div>
    );
}
}

export default ImageUser;
like image 646
Sergio Jimenez Corominas Avatar asked Mar 30 '17 10:03

Sergio Jimenez Corominas


People also ask

How do I know if an image is loaded in React?

forEach((image) => { image. addEventListener("load", () => updateStatus(imagesLoaded), { once: true }); image. addEventListener("error", () => updateStatus(imagesLoaded), { once: true }); }); return; }, [ref]); return status; };

Which methods are called when the state of props of a component is changed?

Render is always called when state or props change. componentWillReceiveProps used to be the way you could do something with the props before the render occurred.

Does state update when props change?

We used the useEffect hook to update the state of a component when its props change. Copied! The logic in the useEffect hook is reran every time one of its dependencies changes. Every time the parentCount prop changes, the useEffect hook is reran and we use the setChildCount function to update the state.


3 Answers

There are several ways to do this, but the simplest is to display the final image hidden, and then flip it to visible once it loads.

JSBin Demo

class Foo extends React.Component {
  constructor(){
    super();
    this.state = {loaded: false};
  }

  render(){
    return (
      <div>
        {this.state.loaded ? null :
          <div
            style={{
              background: 'red',
              height: '400px',
              width: '400px',
            }}
          />
        }
        <img
          style={this.state.loaded ? {} : {display: 'none'}}
          src={this.props.src}
          onLoad={() => this.setState({loaded: true})}
        />
      </div>
    );
  }
}
like image 50
Brigand Avatar answered Oct 24 '22 00:10

Brigand


Same answer as Brigand's accepted answer but with Hooks:

const Foo = ({ src }) => {
  const [loaded, setLoaded] = useState(false);

  return (
    <div>
      {loaded ? null : (
        <div
          style={{
            background: 'red',
            height: '400px',
            width: '400px'
          }}
        />
      )}
      <img
        style={loaded ? {} : { display: 'none' }}
        src={src}
        onLoad={() => setLoaded(true)}
      />
    </div>
  );
};
like image 33
lee_mcmullen Avatar answered Oct 24 '22 01:10

lee_mcmullen


Same idea using reference to the element but using functional component and hooks with typescript:

import React from 'react';

export const Thumbnail = () => {
  const imgEl = React.useRef<HTMLImageElement>(null);
  const [loaded, setLoaded] = React.useState(false);

  const onImageLoaded = () => setLoaded(true);

  React.useEffect(() => {
    const imgElCurrent = imgEl.current;

    if (imgElCurrent) {
      imgElCurrent.addEventListener('load', onImageLoaded);
      return () => imgElCurrent.removeEventListener('load', onImageLoaded);
    }
  }, [imgEl]);

  return (
    <>
      <p style={!loaded ? { display: 'block' } : { display: 'none' }}>
        Loading...
      </p>
      <img
        ref={imgEl}
        src="https://via.placeholder.com/60"
        alt="a placeholder"
        style={loaded ? { display: 'inline-block' } : { display: 'none' }}
      />
    </>
  );
};
like image 10
grll Avatar answered Oct 24 '22 00:10

grll