-
-
Notifications
You must be signed in to change notification settings - Fork 2k
Description
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 |
|---|---|
![]() |
![]() |
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.

