Next.js: Image Events aren't fired

Created on 16 Apr 2019  路  10Comments  路  Source: vercel/next.js

Bug report

Describe the bug

No image event handlers are fired. I can't see the output in the console. When I try it with plain react it works.

To Reproduce

Try the snippet in any component.

          <img
            src="https://lh3.googleusercontent.com/proxy/ogwfaF0iwa05OnTNQFyD0rZ384sAN74p5xwJE6qfJmrEFcmgxlXo4zg22lrlaLcaS_hp9pFCu8s8QZ-GgDy37DxWVOHpq2B4IV35vb4wgHBWfJiYqI_AVARVMaguPane4Raedg=w530-h212-p-rw"
            onError={() => {
              console.log('#############Error##############')
            }}
            onLoad={() => {
              console.log('#############Load##############')
            }}
          />

or this component.

Expected behavior

onLoad or OnError should be executed

System information

  • OS: Ubuntu
  • Browser chrome
  • Version of Next.js: 8.0.4
upstream

Most helpful comment

I did a similar workaround, I explained here:

However, is not 100% ideal because the fallback image is added after hydration and not before. This causes that if the image is loaded before the hydration, first is displaying the broken image status from the browser, and then, after the hydration, is displaying the correct fallback image.

For me the ideal is to catch the image error before the hydration to change to the fallback image ASAP as possible. And avoiding the broken image status from the browser.

All 10 comments

After I had some strange issues with jsx undefined issues during prefetching and route change events I removed it completly and imported my styles with a simple import './styles.scss'. This also fixed this issue. Really strange.

I have a similar issue with the onError. Only is fired sometimes using SSR... Navigating, always is firing the event (always that the image have an error). But the problem start happening reloading the page.

<img {...props} onError={onErrorImage}  />
function onErrorImage(e) {
    e.target.src = fallbackSrc
 }

And sometimes, reloading the page, instead of loading the fallback image... Is loading this:

image

Without firing the onError event... :(

@aralroca I have the exact same issue.

I guess that the problem is that the onError event is fired before the JS is fully downloaded. It is possible? I dunno how to fix it. Any tip?

I guess that the problem is that the onError event is fired before the JS is fully downloaded. It is possible? I dunno how to fix it. Any tip?

I don't think so because I also had issues with onload and the image was definitly available.

@aralroca I could workaound it with:

  const [noImage, setNoImage] = useState(true);
  let imageEl = null;

  const imageRefCb = image => {
    if (image) {
      imageEl = image;
      if (image.complete) {
        setNoImage(false);
      }
    }
  };

  useEffect(() => {
    imageRefCb(imageEl);
  });

           <img
              src={imageUrl}
              alt={name}
              ref={imageRefCb}
              onError={() => setNoImage(true)}
              onLoad={() => setNoImage(false)}
            />

I think it can happen that the image is complete before the event handles are attached.

I did a similar workaround, I explained here:

However, is not 100% ideal because the fallback image is added after hydration and not before. This causes that if the image is loaded before the hydration, first is displaying the broken image status from the browser, and then, after the hydration, is displaying the correct fallback image.

For me the ideal is to catch the image error before the hydration to change to the fallback image ASAP as possible. And avoiding the broken image status from the browser.

``` const [noImage, setNoImage] = useState(true);
let imageEl = null;

const imageRefCb = image => {
if (image) {
imageEl = image;
if (image.complete) {
setNoImage(false);
}
}
};

useEffect(() => {
imageRefCb(imageEl);
});

return (
<>
className="hero__bg-image"
onLoad={() => this.setState({ noImage: false })}
src={/static/imgs/hero/${props.game}.jpg}
alt={props.game}
ref={imageRefCb}
onLoad={() => setNoImage(false)}
/>
className={hero__overlay ${!noImage && "hero__overlay--loaded"}}
/>
```

Even with @starptech's workaround, I'm seeing this code work 100% of the time in Dev, and never on my production server.

I have similar issue with images. OnLoad event works only with external images. For example:

import React, { useState } from "react";

export default props => {
  const [catLoaded, setCatLoaded] = useState(false);
  const [externalPhotoLoaded, setExternalPhotoLoaded] = useState(false);
  return (
    <>
      <div>
        <img
          style={{ width: 400 }}
          src="/static/img/cat.jpg"
          onLoad={() => setCatLoaded}
        />
        {catLoaded ? <p>Local cat is here!</p> : ""}
      </div>
      <div>
        <img
          style={{ width: 400 }}
          src="https://upload.wikimedia.org/wikipedia/commons/5/5e/21-224-5054_NNP_Synevyr_RB_18.jpg"
          onLoad={() => setExternalPhotoLoaded(true)}
        />
        {externalPhotoLoaded ? <p>External photo is here!</p> : ""}
      </div>
    </>
  );
};

Here are two images, one of them is local and another is external, onLoad event fires only on external one, though clearly both are loaded. I have created repository which reproduces this issue. May be someone has a clue for why this is happening and is there a fix for that?

Hello, I have the same issue in my Avatar component. I just want to display a fallback image when the avatar is broken. But, onError is not fire. Here is my code:

class Avatar extends React.Component {
  componentDidMount() {
    this.$el.addEventListener('error', (event) => {
      const fallbackImage = getFallbackImage(
        event.target.getAttribute('src'),
        event.target.dataset,
      )

      event.target.setAttribute('src', fallbackImage)
      event.target.setAttribute('srcset', `${fallbackImage} 1x`)
    }, { once: true })
  }

  render() {
    const { image, size, alt } = this.props
    const className = _trimEnd(`v-avatar ${this.props.className}`)
    const srcAttrs = getImages(image, size)

    return (
      <img
        ref={$el => this.$el = $el} // eslint-disable-line
        {...{ className, ...srcAttrs, alt }}
      />
    )
  }
}

I also tried the other way, but error still is not fire.

class Avatar extends React.Component {
  state = {
    errored: false,
  }

  onError = (event) => {
    if (!this.state.errored) {
      event.target.setAttribute('src', defaultImage)
      event.target.setAttribute('srcset', `${defaultImage} 1x`)

      this.setState({ errored: true })
    }
  }

  render() {
    const { image, size, alt } = this.props
    const className = _trimEnd(`v-avatar ${this.props.className}`)
    const srcAttrs = getImages(image, size)

    return (
      <img
        {...{ className, ...srcAttrs, alt }}
        onError={(event) => this.onError(event)}
      />
    )
  }
}

Anyone here for help?


My temporary solution is using Dynamic Import to wrap the Avatar component and force it only renders in Client-side.

Closing this as it's upstream in React: https://github.com/facebook/react/issues/15446

Was this page helpful?
0 / 5 - 0 ratings

Related issues

swrdfish picture swrdfish  路  3Comments

wagerfield picture wagerfield  路  3Comments

formula349 picture formula349  路  3Comments

rauchg picture rauchg  路  3Comments

olifante picture olifante  路  3Comments