question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

An effect that inserts iframes may run twice in Safari

See original GitHub issue

Reproduction

Here’s a codesandbox showing a Preact effect running twice in Safari: https://codesandbox.io/s/safari-duplicate-effects-b2lnf.

Chrome result Safari result
safari-duplicate-effects - CodeSandbox 2020-11-04 12-58-32 safari-duplicate-effects - CodeSandbox 2020-11-04 13-00-00

The root cause appears to be Safari running pending promises when an iframe is inserted into the DOM. You can verify by running this code in Safari’s dev console:

// Inserting a div results in panda/monkey
defer = Promise.prototype.then.bind(Promise.resolve())
defer(() => console.log("🐵")); document.body.appendChild(document.createElement('div')); console.log("🐼")
🐼
🐵

// Inserting an iframe results in monkey/panda
defer = Promise.prototype.then.bind(Promise.resolve())
defer(() => console.log("🐵")); document.body.appendChild(document.createElement('iframe')); console.log("🐼")
🐵
🐼

As far as I can tell, the exact setup to reproduce is:

  • A parent component that can have its state updated by a child component
  • An effect that updates the parent component’s state
  • Another effect that inserts an iframe into the DOM

The state update enqueues components for re-render, then the iframe insertion causes Safari to immediately kick off a re-render inside Preact’s current render loop. This happens before the current render has cleared effects arrays, so the effects are executed again.

The sandbox looks contrived, but I ran into this while integrating PayPal into a Preact app.

Steps to reproduce

Running the sandbox in Safari 13.1.2 should illustrate the problem. The same sandbox will show one less element in Chrome.

Expected Behavior

Preact should not run an effect with no dependencies twice.

Actual Behavior

Preact runs an effect with no dependencies twice.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:7 (4 by maintainers)

github_iconTop GitHub Comments

6reactions
gaearoncommented, Jan 18, 2022
1reaction
lemonmadecommented, Oct 21, 2021

@developit I was able to resolve this issue in Safari with the following option override:

import {options} from 'preact';
options.requestAnimationFrame = Promise.prototype.then.bind(Promise.resolve().then(() => Promise.resolve()));

Is this what you were suggesting (a double promise resolution)? I assume so but want to make sure I’m not missing something. Would this be something Preact would accept as a contribution? It’s a bit of an unusual one since this mechanism is presumably a bit more expensive than the current requestAnimationFrame implementation, and is only needed for an edge case behaviour in one engine.

I also noticed this pattern is internally used to create a function for deferring updates (https://github.com/preactjs/preact/blob/c7f57db13af43b8cc3353a138639ad9dd9523e16/src/component.js#L181) — do you know if this could be causing any similar issues?

(Sorry for reviving a zombie issue a year after our last comment, at Shopify we got bit by this issue again 😛)

Read more comments on GitHub >

github_iconTop Results From Across the Web

Why are iFrames not being loaded in Safari on some pages?
It seems like a timing thing as we can use javascript to insert the iframe tag into an empty div after the document...
Read more >
The ultimate guide to iframes - LogRocket Blog
Finally, we'll talk about how you can secure your iframe to avoid potential vulnerabilities. What is an iframe, and when do you use...
Read more >
It's time to lazy-load offscreen iframes! - web.dev
Based off Chrome's research into automatically lazy-loading offscreen iframes for Data Saver users, lazy-loading iframes could lead to 2-3% ...
Read more >
Safari Technology Preview Release Notes - Apple Developer
Fixed non-breaking space getting inserted instead of a normal space character between text nodes in contenteditable (257136@main) ...
Read more >
How to embed iframes by bypassing X-Frame-Options and ...
Some websites specify X-Frame-Options in the response header when they want to have control over wher there content can be embedded inside iframe....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found