Search code examples
javascriptreactjsimageyoutubeonerror

Image onError Handling in React


I am trying to grab the maxresdefault of multiple YouTube thumbnails. Some YouTube videos simply don't have a high res thumbnail photo so I want to catch that with an onError prop on an img element. For some reason my function is not triggering when I get the 404 img error. Any ideas? Thanks in advance.

class FeaturedVideo extends Component<Props> {
  addDefaultSrc = (e) => {
    e.target.src = this.props.background.replace("maxresdefault", "hqdefault")
  }

  renderVideo = (props) => (
    <div
      style={{
        width: "100%",
        height: "100%",
        backgroundSize: "contain",
      }}
      className="featured-community-video"
    >
      <img onError={this.addDefaultSrc} src={props.background} alt="" />
      <div className="featured-community-video-title">
        <h2 style={{ fontSize: "0.8em" }}>WATCH</h2>
        <h1 style={{ fontSize: props.titleFontSize }}>{props.title}</h1>
      </div>
    </div>
  )

  render() {
    return (
      <div
        key={this.props.postId}
        style={{
          width: this.props.width,
          height: "50%",
        }}
        className="featured-community-video-container"
      >
        <Link to={routeCodes.POST_DETAILS(this.props.postId)}>
          {this.renderVideo(this.props)}
        </Link>
      </div>
    )
  }
}

Solution

  • To achieve this, I would suggest rendering the <img /> based on the state of your <FeatureVideo/> component.

    You could for instance, create an Image object, attempt to load the background image and by this, reliably determine if the image fails to load. On the images success or failure to load, you would then setState() on your <FeaturedVideo/> component with the appropriate background value which would instead be used to rendering you actual <img/> element:

    class FeaturedVideo extends Component<Props> {
    
      componentDidMount() {
    
        if(this.props.background) {
    
          // When component mounts, create an image object and attempt to load background
          fetch(this.props.background).then(response => {
             if(response.ok) {
               // On success, set the background state from background
               // prop
               this.setState({ background : this.props.background })
             } else {        
               // On failure to load, set the "default" background state
               this.setState({ background : this.props.background.replace("maxresdefault", "hqdefault") })
             }
          });
        }
      }
    
      // Update the function signature to be class method to make state access eaiser
      renderVideo(props) {
    
        return <div
          style={{
            width: "100%",
            height: "100%",
            backgroundSize: "contain",
          }}
          className="featured-community-video">
    
          {/* Update image src to come from state */}
    
          <img src={this.state.background} alt="" />
          <div className="featured-community-video-title">
            <h2 style={{ fontSize: "0.8em" }}>WATCH</h2>
            <h1 style={{ fontSize: props.titleFontSize }}>{props.title}</h1>
          </div>
        </div>
      }
    
      render() {
        return (
          <div
            key={this.props.postId}
            style={{
              width: this.props.width,
              height: "50%",
            }}
            className="featured-community-video-container"
          >
            <Link to={routeCodes.POST_DETAILS(this.props.postId)}>
              {this.renderVideo(this.props)}
            </Link>
          </div>
        )
      }
    }
    

    Hope that helps!