vercel / next.js

The React Framework
https://nextjs.org
MIT License
125.28k stars 26.77k forks source link

iframe.onload event fires before content has fully loaded #39451

Open MartinBlackburn opened 2 years ago

MartinBlackburn commented 2 years ago

Verify canary release

Provide environment information

Operating System: Platform: linux Arch: x64 Version: #45~20.04.1-Ubuntu SMP Mon Apr 4 09:38:31 UTC 2022 Binaries: Node: 14.19.3 npm: 6.14.17 Yarn: 1.22.19 pnpm: N/A Relevant packages: next: 12.1.7-canary.42 react: 17.0.2 react-dom: 17.0.2

What browser are you using? (if relevant)

Chrome Version 103.0.5060.134 (Official Build) (arm64)

How are you deploying your application? (if relevant)

code sandbox

Describe the Bug

The iframe.onload event seems to trigger before the iframe content has fully loaded.

Example site (see console): https://fl6slz.sse.codesandbox.io/

Example code: https://codesandbox.io/s/eloquent-swanson-fl6slz

The parent frame sends a post message on iframe.onload The iframe has an event listener, which doesn't trigger from this event

Expected console output:

loaded
parent {type: 'test', message: 'from child'}
child {type: 'test', message: 'from parent'}
child {type: 'test', message: 'reply from parent'}
child {type: 'test', message: 'from parent timeout'}

Actual console output:

loaded
parent {type: 'test', message: 'from child'}
child {type: 'test', message: 'reply from parent'}
child {type: 'test', message: 'from parent timeout'}

The child does send a post message back to the parent, and the parent can reply successfully. The parent can also send a post message after a second.

This happens on version 12.1.7-canary.42 or above, but doesn't happen on any version below.

Expected Behavior

I would expect version 12.1.7-canary.42 to act like version 12.1.7-canary.41 in terms of sending the iframe.onload event

Link to reproduction

https://fl6slz.sse.codesandbox.io/

To Reproduce

View the site: https://fl6slz.sse.codesandbox.io/ See the console logs are:

loaded
parent {type: 'test', message: 'from child'}
child {type: 'test', message: 'reply from parent'}
child {type: 'test', message: 'from parent timeout'}

Change the version to 12.1.7-canary.41 See the console logs are:

loaded
parent {type: 'test', message: 'from child'}
child {type: 'test', message: 'from parent'}
child {type: 'test', message: 'reply from parent'}
child {type: 'test', message: 'from parent timeout'}
SukkaW commented 2 years ago

It seems that you are adding an event listener during the render phase, which just breaks the fundamentals of React.

In React, any non-render-related tasks are considered un-pure and should only be located in useEffect.

MartinBlackburn commented 2 years ago

Good advice thank you

Unfortunately, the bug still occurs when moving the event listeners into a useEffect - I've updated the example to show this

nctay commented 1 year ago

Same issue. Somehow, onLoad fires once when page gets requested first time and then after page reload never fires again. By the way, @MartinBlackburn, i found temporary workaround. It works ok if src attribute attached inside useEffect

useEffect(() => {
    if (ref.current) {
      ref.current.src = 'https://host.com'
    }
    ref.current?.addEventListener('load', func)
    return () => {
      ref.current?.removeEventListener('load', func)
    }
  }, [])

return <iframe ref={ref}  />