react icon indicating copy to clipboard operation
react copied to clipboard

Bug: <img/> renders twice in firefox with react 18

Open kapibaara opened this issue 3 years ago • 15 comments

React version: 18.2.0

Steps To Reproduce

  1. Insert a tag <img/> with src
  2. Go to Firefox inspect tool to the tab network
  3. Reload the page and watch image request in network

Link to code example: https://codesandbox.io/s/unruffled-jerry-9hli44?file=/src/App.js

The current behavior

There are two request for image in firefox, image renders twice image

The expected behavior

There is one request and one render in firefox

kapibaara avatar Sep 09 '22 13:09 kapibaara

It really happens, even on Chrome/Edge. But I think this is due to StrictMode. Could please try deleting StrictMode on index.js?

It will look like this.

root.render(<App />);

On StrictMode react render twice. https://reactjs.org/docs/strict-mode.html

StrictMode renders components twice (on dev but not production) in order to detect any problems with your code and warn you about them

MiguelMachado-dev avatar Sep 10 '22 03:09 MiguelMachado-dev

It really happens, even on Chrome/Edge. But I think this is due to StrictMode. Could please try deleting StrictMode on index.js?

It will look like this.

root.render(<App />);

On StrictMode react render twice. https://reactjs.org/docs/strict-mode.html

StrictMode renders components twice (on dev but not production) in order to detect any problems with your code and warn you about them

it doesn't help, in my example in codesandbox I tried to remove StrictMode, but the behavior is the same, there are two requests in network tab Аnd on Chrome, if you open this https://c1i5n8.csb.app/ there are no two requests in network tab

kapibaara avatar Sep 12 '22 08:09 kapibaara

There is, check https://codesandbox.io/s/unruffled-jerry-9hli44?file=/src/index.js On Edge and Firefox I still getting 2 requests, disabling strict-mode it stops.

MiguelMachado-dev avatar Sep 12 '22 11:09 MiguelMachado-dev

@MiguelMachado-dev, Just check this example (without StrictMode): https://codesandbox.io/s/goofy-stonebraker-pf0wg7?file=/src/index.js (https://pf0wg7.csb.app/)

And there is screenshot with two requests:

image

What i'm doing wrong?

sigorilla avatar Sep 12 '22 11:09 sigorilla

Not sure, it does not happen to me. We should then wait for more people to see this. It only happen to me when using StrictMode.

Edge: image

Firefox: image

Both browsers up to date.

MiguelMachado-dev avatar Sep 12 '22 12:09 MiguelMachado-dev

It really is an issue. Where can we find the root cause of this problem?

arpitj007 avatar Sep 12 '22 16:09 arpitj007

@MiguelMachado-dev How can I contribute to fixing this bug?

arpitj007 avatar Sep 12 '22 17:09 arpitj007

@MiguelMachado-dev How can I contribute to fixing this bug?

Do you know how to fix it?

If you know, you can fork the project and open a PR. Or a solution, you can comment here how he can fix it properly.

MiguelMachado-dev avatar Sep 12 '22 18:09 MiguelMachado-dev

Hi! I repeated this case in https://codesandbox.io/ with clean HTML and get same result, then I try same code with corgi in https://codepen.io/sult4novars/pen/NWMRzRX and get one request)

sult4novars avatar Sep 13 '22 10:09 sult4novars

Maybe it depends on OS? I have MacOS 12 (M1), Firefox 104.0.2

And I reproduced it on codepen: https://codepen.io/sult4novars/pen/NWMRzRX

sigorilla avatar Sep 13 '22 10:09 sigorilla

Maybe it depends on OS? I have MacOS 12 (M1), Firefox 104.0.2

And I reproduced it on codepen: https://codepen.io/sult4novars/pen/NWMRzRX

i have same params MacOS 12.6 (M1, 8gb ), Firefox 104.0.2

image

sult4novars avatar Sep 13 '22 11:09 sult4novars

Maybe it depends on OS? I have MacOS 12 (M1), Firefox 104.0.2

And I reproduced it on codepen: https://codepen.io/sult4novars/pen/NWMRzRX

ooo I reproduced to but when I reload page this case not reproduced but i check status and see NS_BINDING_ABORTED and search this status, and what I found https://stackoverflow.com/questions/704561/ns-binding-aborted-shown-in-firefox-with-httpfox

sult4novars avatar Sep 13 '22 11:09 sult4novars

Maybe it depends on OS? I have MacOS 12 (M1), Firefox 104.0.2

And I reproduced it on codepen: https://codepen.io/sult4novars/pen/NWMRzRX

try with new image https://codepen.io/sult4novars/pen/NWMRzRX

when I first reload: image

when i try to reload many times image

sult4novars avatar Sep 13 '22 11:09 sult4novars

when i try my example with react 17, there are not two requests in firefox https://codesandbox.io/s/wizardly-lena-yobcgz?file=/src/index.js

kapibaara avatar Sep 13 '22 13:09 kapibaara

Firefox is simply ignoring the cache expires value when you force reload the page. The NS_BINDING_ABORTED tries to indicate that the browser checks whether the img is stored in the cdn cache which is obviously not in this case as there are no extra cache expires headers being sent.

The request gets blocked because the page(codepen here) hasnt loaded yet and there is already another request (get image) being sent while the page request is yet to return completely. This isnt a typical React bug IMHO but more of a failsafe method Firefox implemented.

You can easily see the next reload request sent for the same img is served via cache as cache gets set to current whenever missing an expiry header. Have a look at the stack trace in dev console

image

varunmulay22 avatar Oct 10 '22 03:10 varunmulay22

We are observing the same behaviour. Firefox 115, Ubuntu Linux 23.04 and macOs. Unable to test on Windows yet.

Here is some details.

The first request is initiated in react-dom.development.js, line 855:

node.setAttribute("src", "https://.../korgi-na-trave-960x540-1-960x540.jpg");

Here is the stack trace.

setValueForProperty           react-dom.development.js:855:12
setInitialDOMProperties       react-dom.development.js:9720:26
setInitialProperties          react-dom.development.js:9921:26
finalizeInitialChildren       react-dom.development.js:10950:23
completeWork                  react-dom.development.js:22193:40
completeUnitOfWork            react-dom.development.js:26596:16
performUnitOfWork             react-dom.development.js:26568:23
workLoopSync                  react-dom.development.js:26466:22
renderRootSync                react-dom.development.js:26434:7
performConcurrentWorkOnRoot   react-dom.development.js:25738:74
...

The second request is initiated here:

function commitMount(domElement, type, newProps, internalInstanceHandle) {
  // Despite the naming that might imply otherwise, this method only
  // fires if there is an `Update` effect scheduled during mounting.
  // This happens if `finalizeInitialChildren` returns `true` (which it
  // does to implement the `autoFocus` attribute on the client). But
  // there are also other cases when this might happen (such as patching
  // up text content during hydration mismatch). So we'll check this again.
  switch (type) {
    case 'button':
    case 'input':
    case 'select':
    case 'textarea':
      if (newProps.autoFocus) {
        domElement.focus();
      }

      return;

    case 'img':
      {
        if (newProps.src) {
          domElement.src = newProps.src; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< HERE
        }

        return;
      }
  }
}
commitMount                       react-dom.development.js:11038
commitLayoutEffectOnFiber         react-dom.development.js:23407
commitLayoutMountEffects_complete react-dom.development.js:24688
commitLayoutEffects_begin         react-dom.development.js:24674
commitLayoutEffects_begin         react-dom.development.js:24656
commitLayoutEffects               react-dom.development.js:24612
commitRootImpl                    react-dom.development.js:26823
commitRoot                        react-dom.development.js:26682
finishConcurrentRender            react-dom.development.js:25981
performConcurrentWorkOnRoot       react-dom.development.js:25809
...

node, in the first snippet, and domElement, in the 2nd, are the very same variable: the real <img> DOM node.

The point is the browser is replacing the source of the imgage twice. If the first request has not been completed when the src attribute is set the second time, the browser aborts the first request, leading to the NS_BINDING_ABORTED error. This occurs despite the src value is the same both time.

Note that the src is always converted to a fully qualified URL (have a look : https://github.com/facebook/react/commit/f0dd459e0d97081cb3c313ec52285e3e422f8dbf):

const n = document.createElement("img");
n.src = "/favicon.ico"
console.log(n.src); // prints https://wherever.you.are/favicon.ico

This behaviour has been introduced in https://github.com/facebook/react/commit/086fa8ee2f80f0dc34b7d145be72f9843fca975d

Using Chromium, the request is only initated once, at the very end of the process.

maxencelaurent avatar Jul 07 '23 12:07 maxencelaurent

This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!

github-actions[bot] avatar Apr 10 '24 01:04 github-actions[bot]

Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you!

github-actions[bot] avatar Apr 17 '24 11:04 github-actions[bot]

@maxencelaurent I am observing the very same behavior you describe. Have you been able to solve or workaround it somehow? Thanks!

stanleyk avatar Apr 17 '24 13:04 stanleyk

I have the same issue. image I does not happen in Chrome, but in Firefox 126.0b4 (64-bit)

wuarmin avatar Apr 24 '24 16:04 wuarmin

I can reproduce this as well in Firefox 126.0 (and older versions as well), as a previous comment mentioned this is caused by the "fix" where commitMount() sets the src attribute to the same value to trigger load/error events at the correct time.

Changing in ReactDOMHostConfig.js (for React 18.3.1)

from

if ((newProps: any).src) {
    ((domElement: any): HTMLImageElement).src = (newProps: any).src;
}

to

if ((newProps: any).src && ((domElement: any): HTMLImageElement).complete !== false) {
    ((domElement: any): HTMLImageElement).src = (newProps: any).src;
}

fixes it for me and the events still get fired, however I am not sure if this fix is safe in all cases and browsers

faulpeltz avatar May 17 '24 14:05 faulpeltz