Skip to content

An effect that inserts iframes may run twice in Safari #2814

@GoodForOneFare

Description

@GoodForOneFare

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions