Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Firefox flash of white on next/image with placeholder=blur #35889

Merged
merged 6 commits into from Apr 5, 2022

Conversation

styfle
Copy link
Member

@styfle styfle commented Apr 4, 2022

History

In PR #24153, placeholder=blur was added and it set element.style.backgroundImage = 'none' instead of using react state to re-render. Then in PR #25916, a delay was added to handle removing the blur placeholder. Then in PR #25994, img.decode() was utilized but we found this caused problems in Firefox in #26011.

Today

This PR changes the the blur placeholder removal to use react state to re-render. This should prevent Firefox from erroring although we should probably keep the catch() just in case. This was pointed out when React 18 caused subtle differences in Firefox in this comment #30128 (comment)

@ijjk ijjk added created-by: Next.js team PRs by the Next.js team type: next labels Apr 4, 2022
@styfle styfle changed the title Fix firefox flash of white on next/image with placeholder=blur Fix Firefox flash of white on next/image with placeholder=blur Apr 4, 2022
@ijjk

This comment has been minimized.

@ijjk

This comment has been minimized.

@ijjk

This comment has been minimized.

@ijjk
Copy link
Member

ijjk commented Apr 5, 2022

Stats from current PR

Default Build (Increase detected ⚠️)
General Overall increase ⚠️
vercel/next.js canary vercel/next.js next-image-remove-blur-without-ref Change
buildDuration 15.1s 15.2s ⚠️ +129ms
buildDurationCached 6s 6s -11ms
nodeModulesSize 477 MB 477 MB ⚠️ +887 B
Page Load Tests Overall increase ✓
vercel/next.js canary vercel/next.js next-image-remove-blur-without-ref Change
/ failed reqs 0 0
/ total time (seconds) 2.936 2.947 ⚠️ +0.01
/ avg req/sec 851.37 848.31 ⚠️ -3.06
/error-in-render failed reqs 0 0
/error-in-render total time (seconds) 1.196 1.189 -0.01
/error-in-render avg req/sec 2089.75 2103.37 +13.62
Client Bundles (main, webpack)
vercel/next.js canary vercel/next.js next-image-remove-blur-without-ref Change
925.HASH.js gzip 179 B 179 B
framework-HASH.js gzip 42 kB 42 kB
main-HASH.js gzip 28.5 kB 28.5 kB
webpack-HASH.js gzip 1.44 kB 1.44 kB
Overall change 72.1 kB 72.1 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary vercel/next.js next-image-remove-blur-without-ref Change
polyfills-HASH.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages Overall increase ⚠️
vercel/next.js canary vercel/next.js next-image-remove-blur-without-ref Change
_app-HASH.js gzip 1.36 kB 1.36 kB
_error-HASH.js gzip 192 B 192 B
amp-HASH.js gzip 309 B 309 B
css-HASH.js gzip 327 B 327 B
dynamic-HASH.js gzip 3.05 kB 3.05 kB
head-HASH.js gzip 351 B 351 B
hooks-HASH.js gzip 920 B 920 B
image-HASH.js gzip 5.52 kB 5.64 kB ⚠️ +124 B
index-HASH.js gzip 263 B 263 B
link-HASH.js gzip 2.32 kB 2.32 kB
routerDirect..HASH.js gzip 320 B 320 B
script-HASH.js gzip 387 B 387 B
withRouter-HASH.js gzip 319 B 319 B
85e02e95b279..7e3.css gzip 107 B 107 B
Overall change 15.7 kB 15.9 kB ⚠️ +124 B
Client Build Manifests Overall decrease ✓
vercel/next.js canary vercel/next.js next-image-remove-blur-without-ref Change
_buildManifest.js gzip 460 B 459 B -1 B
Overall change 460 B 459 B -1 B
Rendered Page Sizes
vercel/next.js canary vercel/next.js next-image-remove-blur-without-ref Change
index.html gzip 532 B 532 B
link.html gzip 545 B 545 B
withRouter.html gzip 526 B 526 B
Overall change 1.6 kB 1.6 kB

Diffs

Diff for _buildManifest.js
@@ -12,7 +12,7 @@ self.__BUILD_MANIFEST = {
   ],
   "/head": ["static\u002Fchunks\u002Fpages\u002Fhead-96a5d6ed07cf5a83.js"],
   "/hooks": ["static\u002Fchunks\u002Fpages\u002Fhooks-9dfe734f583d4926.js"],
-  "/image": ["static\u002Fchunks\u002Fpages\u002Fimage-3503c757a30d7190.js"],
+  "/image": ["static\u002Fchunks\u002Fpages\u002Fimage-f39504b2095f9b7d.js"],
   "/link": ["static\u002Fchunks\u002Fpages\u002Flink-a72b8ab130103a8f.js"],
   "/routerDirect": [
     "static\u002Fchunks\u002Fpages\u002FrouterDirect-cb15c8ac2322bf32.js"
Diff for image-HASH.js
@@ -154,6 +154,7 @@
           objectFit = _param.objectFit,
           objectPosition = _param.objectPosition,
           onLoadingComplete = _param.onLoadingComplete,
+          onError = _param.onError,
           _loader = _param.loader,
           loader = _loader === void 0 ? defaultImageLoader : _loader,
           _placeholder = _param.placeholder,
@@ -175,6 +176,7 @@
             "objectFit",
             "objectPosition",
             "onLoadingComplete",
+            "onError",
             "loader",
             "placeholder",
             "blurDataURL"
@@ -247,6 +249,9 @@
         if (true && loadedImageURLs.has(src)) {
           isLazy = false;
         }
+        var ref2 = _slicedToArray((0, _react).useState(false), 2),
+          blurComplete = ref2[0],
+          setBlurComplete = ref2[1];
         var ref1 = _slicedToArray(
             (0, _useIntersection).useIntersection({
               rootRef: lazyRoot,
@@ -319,7 +324,7 @@
             : layoutStyle
         );
         var blurStyle =
-          placeholder === "blur"
+          placeholder === "blur" && !blurComplete
             ? {
                 filter: "blur(20px)",
                 backgroundSize: objectFit || "cover",
@@ -406,7 +411,6 @@
         var useLayoutEffect = false ? 0 : _react.default.useLayoutEffect;
         var onLoadingCompleteRef = (0, _react).useRef(onLoadingComplete);
         var previousImageSrc = (0, _react).useRef(src);
-        var imgRef = (0, _react).useRef(null);
         (0, _react).useEffect(
           function() {
             onLoadingCompleteRef.current = onLoadingComplete;
@@ -419,21 +423,8 @@
               resetIntersected();
               previousImageSrc.current = src;
             }
-            setIntersection(imgRef.current);
           },
-          [setIntersection, resetIntersected, src]
-        );
-        (0, _react).useEffect(
-          function() {
-            handleLoading(
-              imgRef,
-              srcString,
-              layout,
-              placeholder,
-              onLoadingCompleteRef
-            );
-          },
-          [srcString, layout, placeholder, isVisible]
+          [resetIntersected, src]
         );
         var imgElementArgs = _objectSpread(
           {
@@ -446,13 +437,16 @@
             className: className,
             imgStyle: imgStyle,
             blurStyle: blurStyle,
-            imgRef: imgRef,
             loading: loading,
             config: config,
             unoptimized: unoptimized,
             placeholder: placeholder,
             loader: loader,
-            srcString: srcString
+            srcString: srcString,
+            onLoadingCompleteRef: onLoadingCompleteRef,
+            setBlurComplete: setBlurComplete,
+            setIntersection: setIntersection,
+            isVisible: isVisible
           },
           rest
         );
@@ -758,9 +752,9 @@
             sizes: undefined
           };
         }
-        var ref2 = getWidths(config, width, layout, sizes),
-          widths = ref2.widths,
-          kind = ref2.kind;
+        var ref3 = getWidths(config, width, layout, sizes),
+          widths = ref3.widths,
+          kind = ref3.kind;
         var last = widths.length - 1;
         return {
           sizes: !sizes && kind === "w" ? "100vw" : sizes,
@@ -822,56 +816,53 @@
       // See https://stackoverflow.com/q/39777833/266535 for why we use this ref
       // handler instead of the img's onLoad attribute.
       function handleLoading(
-        imgRef,
+        img,
         src,
         layout,
         placeholder,
-        onLoadingCompleteRef
+        onLoadingCompleteRef,
+        setBlurComplete
       ) {
-        var handleLoad = function() {
-          var img = imgRef.current;
-          if (!img) {
+        if (
+          !img ||
+          img.src === emptyDataURL ||
+          img["data-loaded-src"] === src
+        ) {
+          return;
+        }
+        img["data-loaded-src"] = src;
+        var p = "decode" in img ? img.decode() : Promise.resolve();
+        p.catch(function() {}).then(function() {
+          if (!img.parentNode) {
+            // Exit early in case of race condition:
+            // - onload() is called
+            // - decode() is called but incomplete
+            // - unmount is called
+            // - decode() completes
             return;
           }
-          if (img.src !== emptyDataURL) {
-            var p = "decode" in img ? img.decode() : Promise.resolve();
-            p.catch(function() {}).then(function() {
-              if (!imgRef.current) {
-                return;
-              }
-              loadedImageURLs.add(src);
-              if (placeholder === "blur") {
-                img.style.filter = "";
-                img.style.backgroundSize = "";
-                img.style.backgroundImage = "";
-                img.style.backgroundPosition = "";
-              }
-              if (onLoadingCompleteRef.current) {
-                var naturalWidth = img.naturalWidth,
-                  naturalHeight = img.naturalHeight;
-                // Pass back read-only primitive values but not the
-                // underlying DOM element because it could be misused.
-                onLoadingCompleteRef.current({
-                  naturalWidth: naturalWidth,
-                  naturalHeight: naturalHeight
-                });
-              }
-              if (false) {
-                var parent, ref3;
-              }
+          loadedImageURLs.add(src);
+          if (placeholder === "blur") {
+            setBlurComplete(true);
+          }
+          if (
+            onLoadingCompleteRef === null || onLoadingCompleteRef === void 0
+              ? void 0
+              : onLoadingCompleteRef.current
+          ) {
+            var naturalWidth = img.naturalWidth,
+              naturalHeight = img.naturalHeight;
+            // Pass back read-only primitive values but not the
+            // underlying DOM element because it could be misused.
+            onLoadingCompleteRef.current({
+              naturalWidth: naturalWidth,
+              naturalHeight: naturalHeight
             });
           }
-        };
-        if (imgRef.current) {
-          if (imgRef.current.complete) {
-            // If the real image fails to load, this will still remove the placeholder.
-            // This is the desired behavior for now, and will be revisited when error
-            // handling is worked on for the image component itself.
-            handleLoad();
-          } else {
-            imgRef.current.onload = handleLoad;
+          if (false) {
+            var parent, ref3;
           }
-        }
+        });
       }
       var ImageElement = function(_param) {
         var imgAttributes = _param.imgAttributes,
@@ -883,13 +874,17 @@
           imgStyle = _param.imgStyle,
           blurStyle = _param.blurStyle,
           isLazy = _param.isLazy,
-          imgRef = _param.imgRef,
           placeholder = _param.placeholder,
           loading = _param.loading,
           srcString = _param.srcString,
           config = _param.config,
           unoptimized = _param.unoptimized,
           loader = _param.loader,
+          onLoadingCompleteRef = _param.onLoadingCompleteRef,
+          setBlurComplete = _param.setBlurComplete,
+          setIntersection = _param.setIntersection,
+          onError = _param.onError,
+          isVisible = _param.isVisible,
           rest = _objectWithoutProperties(_param, [
             "imgAttributes",
             "heightInt",
@@ -900,13 +895,17 @@
             "imgStyle",
             "blurStyle",
             "isLazy",
-            "imgRef",
             "placeholder",
             "loading",
             "srcString",
             "config",
             "unoptimized",
-            "loader"
+            "loader",
+            "onLoadingCompleteRef",
+            "setBlurComplete",
+            "setIntersection",
+            "onError",
+            "isVisible"
           ]);
         return /*#__PURE__*/ _react.default.createElement(
           _react.default.Fragment,
@@ -927,8 +926,52 @@
                 decoding: "async",
                 "data-nimg": layout,
                 className: className,
-                ref: imgRef,
-                style: _objectSpread({}, imgStyle, blurStyle)
+                style: _objectSpread({}, imgStyle, blurStyle),
+                ref: (0, _react).useCallback(
+                  function(img) {
+                    setIntersection(img);
+                    if (
+                      img === null || img === void 0 ? void 0 : img.complete
+                    ) {
+                      handleLoading(
+                        img,
+                        srcString,
+                        layout,
+                        placeholder,
+                        onLoadingCompleteRef,
+                        setBlurComplete
+                      );
+                    }
+                  },
+                  [
+                    setIntersection,
+                    srcString,
+                    layout,
+                    placeholder,
+                    onLoadingCompleteRef,
+                    setBlurComplete
+                  ]
+                ),
+                onLoad: function(event) {
+                  var img = event.currentTarget;
+                  handleLoading(
+                    img,
+                    srcString,
+                    layout,
+                    placeholder,
+                    onLoadingCompleteRef,
+                    setBlurComplete
+                  );
+                },
+                onError: function(event) {
+                  if (placeholder === "blur") {
+                    // If the real image fails to load, this will still remove the placeholder.
+                    setBlurComplete(true);
+                  }
+                  if (onError) {
+                    onError(event);
+                  }
+                }
               }
             )
           ),

Default Build with SWC (Increase detected ⚠️)
General Overall increase ⚠️
vercel/next.js canary vercel/next.js next-image-remove-blur-without-ref Change
buildDuration 17.7s 17.8s ⚠️ +106ms
buildDurationCached 6s 6s ⚠️ +2ms
nodeModulesSize 477 MB 477 MB ⚠️ +887 B
Page Load Tests Overall decrease ⚠️
vercel/next.js canary vercel/next.js next-image-remove-blur-without-ref Change
/ failed reqs 0 0
/ total time (seconds) 2.932 2.938 ⚠️ +0.01
/ avg req/sec 852.68 850.95 ⚠️ -1.73
/error-in-render failed reqs 0 0
/error-in-render total time (seconds) 1.176 1.191 ⚠️ +0.02
/error-in-render avg req/sec 2125.04 2098.68 ⚠️ -26.36
Client Bundles (main, webpack)
vercel/next.js canary vercel/next.js next-image-remove-blur-without-ref Change
925.HASH.js gzip 178 B 178 B
framework-HASH.js gzip 42.3 kB 42.3 kB
main-HASH.js gzip 28.8 kB 28.8 kB
webpack-HASH.js gzip 1.45 kB 1.45 kB
Overall change 72.7 kB 72.7 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary vercel/next.js next-image-remove-blur-without-ref Change
polyfills-HASH.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages Overall increase ⚠️
vercel/next.js canary vercel/next.js next-image-remove-blur-without-ref Change
_app-HASH.js gzip 1.35 kB 1.35 kB
_error-HASH.js gzip 179 B 179 B
amp-HASH.js gzip 313 B 313 B
css-HASH.js gzip 325 B 325 B
dynamic-HASH.js gzip 3.03 kB 3.03 kB
head-HASH.js gzip 351 B 351 B
hooks-HASH.js gzip 921 B 921 B
image-HASH.js gzip 5.63 kB 5.7 kB ⚠️ +75 B
index-HASH.js gzip 261 B 261 B
link-HASH.js gzip 2.38 kB 2.38 kB
routerDirect..HASH.js gzip 322 B 322 B
script-HASH.js gzip 388 B 388 B
withRouter-HASH.js gzip 317 B 317 B
85e02e95b279..7e3.css gzip 107 B 107 B
Overall change 15.9 kB 15.9 kB ⚠️ +75 B
Client Build Manifests Overall decrease ✓
vercel/next.js canary vercel/next.js next-image-remove-blur-without-ref Change
_buildManifest.js gzip 460 B 459 B -1 B
Overall change 460 B 459 B -1 B
Rendered Page Sizes
vercel/next.js canary vercel/next.js next-image-remove-blur-without-ref Change
index.html gzip 531 B 531 B
link.html gzip 544 B 544 B
withRouter.html gzip 527 B 527 B
Overall change 1.6 kB 1.6 kB

Diffs

Diff for _buildManifest.js
@@ -12,7 +12,7 @@ self.__BUILD_MANIFEST = {
   ],
   "/head": ["static\u002Fchunks\u002Fpages\u002Fhead-96a5d6ed07cf5a83.js"],
   "/hooks": ["static\u002Fchunks\u002Fpages\u002Fhooks-9dfe734f583d4926.js"],
-  "/image": ["static\u002Fchunks\u002Fpages\u002Fimage-3503c757a30d7190.js"],
+  "/image": ["static\u002Fchunks\u002Fpages\u002Fimage-f39504b2095f9b7d.js"],
   "/link": ["static\u002Fchunks\u002Fpages\u002Flink-a72b8ab130103a8f.js"],
   "/routerDirect": [
     "static\u002Fchunks\u002Fpages\u002FrouterDirect-cb15c8ac2322bf32.js"
Diff for image-HASH.js
@@ -154,6 +154,7 @@
           objectFit = _param.objectFit,
           objectPosition = _param.objectPosition,
           onLoadingComplete = _param.onLoadingComplete,
+          onError = _param.onError,
           _loader = _param.loader,
           loader = _loader === void 0 ? defaultImageLoader : _loader,
           _placeholder = _param.placeholder,
@@ -175,6 +176,7 @@
             "objectFit",
             "objectPosition",
             "onLoadingComplete",
+            "onError",
             "loader",
             "placeholder",
             "blurDataURL"
@@ -247,6 +249,9 @@
         if (true && loadedImageURLs.has(src)) {
           isLazy = false;
         }
+        var ref2 = _slicedToArray((0, _react).useState(false), 2),
+          blurComplete = ref2[0],
+          setBlurComplete = ref2[1];
         var ref1 = _slicedToArray(
             (0, _useIntersection).useIntersection({
               rootRef: lazyRoot,
@@ -319,7 +324,7 @@
             : layoutStyle
         );
         var blurStyle =
-          placeholder === "blur"
+          placeholder === "blur" && !blurComplete
             ? {
                 filter: "blur(20px)",
                 backgroundSize: objectFit || "cover",
@@ -406,7 +411,6 @@
         var useLayoutEffect = false ? 0 : _react.default.useLayoutEffect;
         var onLoadingCompleteRef = (0, _react).useRef(onLoadingComplete);
         var previousImageSrc = (0, _react).useRef(src);
-        var imgRef = (0, _react).useRef(null);
         (0, _react).useEffect(
           function() {
             onLoadingCompleteRef.current = onLoadingComplete;
@@ -419,21 +423,8 @@
               resetIntersected();
               previousImageSrc.current = src;
             }
-            setIntersection(imgRef.current);
           },
-          [setIntersection, resetIntersected, src]
-        );
-        (0, _react).useEffect(
-          function() {
-            handleLoading(
-              imgRef,
-              srcString,
-              layout,
-              placeholder,
-              onLoadingCompleteRef
-            );
-          },
-          [srcString, layout, placeholder, isVisible]
+          [resetIntersected, src]
         );
         var imgElementArgs = _objectSpread(
           {
@@ -446,13 +437,16 @@
             className: className,
             imgStyle: imgStyle,
             blurStyle: blurStyle,
-            imgRef: imgRef,
             loading: loading,
             config: config,
             unoptimized: unoptimized,
             placeholder: placeholder,
             loader: loader,
-            srcString: srcString
+            srcString: srcString,
+            onLoadingCompleteRef: onLoadingCompleteRef,
+            setBlurComplete: setBlurComplete,
+            setIntersection: setIntersection,
+            isVisible: isVisible
           },
           rest
         );
@@ -758,9 +752,9 @@
             sizes: undefined
           };
         }
-        var ref2 = getWidths(config, width, layout, sizes),
-          widths = ref2.widths,
-          kind = ref2.kind;
+        var ref3 = getWidths(config, width, layout, sizes),
+          widths = ref3.widths,
+          kind = ref3.kind;
         var last = widths.length - 1;
         return {
           sizes: !sizes && kind === "w" ? "100vw" : sizes,
@@ -822,56 +816,53 @@
       // See https://stackoverflow.com/q/39777833/266535 for why we use this ref
       // handler instead of the img's onLoad attribute.
       function handleLoading(
-        imgRef,
+        img,
         src,
         layout,
         placeholder,
-        onLoadingCompleteRef
+        onLoadingCompleteRef,
+        setBlurComplete
       ) {
-        var handleLoad = function() {
-          var img = imgRef.current;
-          if (!img) {
+        if (
+          !img ||
+          img.src === emptyDataURL ||
+          img["data-loaded-src"] === src
+        ) {
+          return;
+        }
+        img["data-loaded-src"] = src;
+        var p = "decode" in img ? img.decode() : Promise.resolve();
+        p.catch(function() {}).then(function() {
+          if (!img.parentNode) {
+            // Exit early in case of race condition:
+            // - onload() is called
+            // - decode() is called but incomplete
+            // - unmount is called
+            // - decode() completes
             return;
           }
-          if (img.src !== emptyDataURL) {
-            var p = "decode" in img ? img.decode() : Promise.resolve();
-            p.catch(function() {}).then(function() {
-              if (!imgRef.current) {
-                return;
-              }
-              loadedImageURLs.add(src);
-              if (placeholder === "blur") {
-                img.style.filter = "";
-                img.style.backgroundSize = "";
-                img.style.backgroundImage = "";
-                img.style.backgroundPosition = "";
-              }
-              if (onLoadingCompleteRef.current) {
-                var naturalWidth = img.naturalWidth,
-                  naturalHeight = img.naturalHeight;
-                // Pass back read-only primitive values but not the
-                // underlying DOM element because it could be misused.
-                onLoadingCompleteRef.current({
-                  naturalWidth: naturalWidth,
-                  naturalHeight: naturalHeight
-                });
-              }
-              if (false) {
-                var parent, ref3;
-              }
+          loadedImageURLs.add(src);
+          if (placeholder === "blur") {
+            setBlurComplete(true);
+          }
+          if (
+            onLoadingCompleteRef === null || onLoadingCompleteRef === void 0
+              ? void 0
+              : onLoadingCompleteRef.current
+          ) {
+            var naturalWidth = img.naturalWidth,
+              naturalHeight = img.naturalHeight;
+            // Pass back read-only primitive values but not the
+            // underlying DOM element because it could be misused.
+            onLoadingCompleteRef.current({
+              naturalWidth: naturalWidth,
+              naturalHeight: naturalHeight
             });
           }
-        };
-        if (imgRef.current) {
-          if (imgRef.current.complete) {
-            // If the real image fails to load, this will still remove the placeholder.
-            // This is the desired behavior for now, and will be revisited when error
-            // handling is worked on for the image component itself.
-            handleLoad();
-          } else {
-            imgRef.current.onload = handleLoad;
+          if (false) {
+            var parent, ref3;
           }
-        }
+        });
       }
       var ImageElement = function(_param) {
         var imgAttributes = _param.imgAttributes,
@@ -883,13 +874,17 @@
           imgStyle = _param.imgStyle,
           blurStyle = _param.blurStyle,
           isLazy = _param.isLazy,
-          imgRef = _param.imgRef,
           placeholder = _param.placeholder,
           loading = _param.loading,
           srcString = _param.srcString,
           config = _param.config,
           unoptimized = _param.unoptimized,
           loader = _param.loader,
+          onLoadingCompleteRef = _param.onLoadingCompleteRef,
+          setBlurComplete = _param.setBlurComplete,
+          setIntersection = _param.setIntersection,
+          onError = _param.onError,
+          isVisible = _param.isVisible,
           rest = _objectWithoutProperties(_param, [
             "imgAttributes",
             "heightInt",
@@ -900,13 +895,17 @@
             "imgStyle",
             "blurStyle",
             "isLazy",
-            "imgRef",
             "placeholder",
             "loading",
             "srcString",
             "config",
             "unoptimized",
-            "loader"
+            "loader",
+            "onLoadingCompleteRef",
+            "setBlurComplete",
+            "setIntersection",
+            "onError",
+            "isVisible"
           ]);
         return /*#__PURE__*/ _react.default.createElement(
           _react.default.Fragment,
@@ -927,8 +926,52 @@
                 decoding: "async",
                 "data-nimg": layout,
                 className: className,
-                ref: imgRef,
-                style: _objectSpread({}, imgStyle, blurStyle)
+                style: _objectSpread({}, imgStyle, blurStyle),
+                ref: (0, _react).useCallback(
+                  function(img) {
+                    setIntersection(img);
+                    if (
+                      img === null || img === void 0 ? void 0 : img.complete
+                    ) {
+                      handleLoading(
+                        img,
+                        srcString,
+                        layout,
+                        placeholder,
+                        onLoadingCompleteRef,
+                        setBlurComplete
+                      );
+                    }
+                  },
+                  [
+                    setIntersection,
+                    srcString,
+                    layout,
+                    placeholder,
+                    onLoadingCompleteRef,
+                    setBlurComplete
+                  ]
+                ),
+                onLoad: function(event) {
+                  var img = event.currentTarget;
+                  handleLoading(
+                    img,
+                    srcString,
+                    layout,
+                    placeholder,
+                    onLoadingCompleteRef,
+                    setBlurComplete
+                  );
+                },
+                onError: function(event) {
+                  if (placeholder === "blur") {
+                    // If the real image fails to load, this will still remove the placeholder.
+                    setBlurComplete(true);
+                  }
+                  if (onError) {
+                    onError(event);
+                  }
+                }
               }
             )
           ),
Commit: 4f13d4f

@kodiakhq kodiakhq bot merged commit cec388c into canary Apr 5, 2022
@kodiakhq kodiakhq bot deleted the next-image-remove-blur-without-ref branch April 5, 2022 22:47
kodiakhq bot pushed a commit that referenced this pull request Apr 15, 2022
Even though we never documented it, the `onLoad` prop used to work (in most cases) in Next.js 12.1.4 and broke in 12.1.5 once the code was refactored in PR #35889.

We still shouldn't document it because `onLoad` doesn't work in all cases so [`onLoadingComplete`](https://nextjs.org/docs/api-reference/next/image#onloadingcomplete) is preferred, however tests were added for the cases that do work.

Use cases that don't work with `onLoad` are Data URLs as well as `loading="eager"`.
@shinkj11 shinkj11 mentioned this pull request Apr 20, 2022
3 tasks
kodiakhq bot pushed a commit that referenced this pull request Apr 22, 2022
## History
In PR #35889, `onError` prop was added explicitly on the `Image` component, but it was ommitted from the `ImageElement` component. In result, the callback didn't work at all.

## Now
This PR deletes the `onError` prop from explicitly defined props. Explicitly adding `onError` on the `const imgElementArgs` is considered, but deleting the defined prop from `Image` is more reasonable because the prop isn't used in `Image` component.


## Bug

- [x] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Errors have helpful link attached, see `contributing.md`

Co-authored-by: Steven <229881+styfle@users.noreply.github.com>
@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 6, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants