diff --git a/compat/src/portals.js b/compat/src/portals.js
index c480a3d3a3..b32353bbfd 100644
--- a/compat/src/portals.js
+++ b/compat/src/portals.js
@@ -32,6 +32,12 @@ function Portal(props) {
}
if (!_this._temp) {
+ // Ensure the element has a mask for useId invocations
+ let root = _this._vnode;
+ while (root !== null && !root._mask && root._parent !== null) {
+ root = root._parent;
+ }
+
_this._container = container;
// Create a fake DOM parent node that manages a subset of `container`'s children:
@@ -39,6 +45,7 @@ function Portal(props) {
nodeType: 1,
parentNode: container,
childNodes: [],
+ _children: { _mask: root._mask },
contains: () => true,
// Technically this isn't needed
appendChild(child) {
diff --git a/compat/test/browser/portals.test.js b/compat/test/browser/portals.test.js
index d005498f87..51a0111b0f 100644
--- a/compat/test/browser/portals.test.js
+++ b/compat/test/browser/portals.test.js
@@ -5,10 +5,12 @@ import React, {
useState,
Component,
useEffect,
- Fragment
+ Fragment,
+ useId
} from 'preact/compat';
import { setupScratch, teardown } from '../../../test/_util/helpers';
import { setupRerender, act } from 'preact/test-utils';
+import { expect } from 'chai';
/* eslint-disable react/jsx-boolean-value, react/display-name, prefer-arrow-callback */
@@ -212,6 +214,38 @@ describe('Portal', () => {
expect(scratch.firstChild.firstChild.childNodes.length).to.equal(0);
});
+ it('should have unique ids for each portal', () => {
+ let root = document.createElement('div');
+ let dialog = document.createElement('div');
+ dialog.id = 'container';
+
+ scratch.appendChild(root);
+ scratch.appendChild(dialog);
+
+ function Id() {
+ const id = useId();
+ return id;
+ }
+
+ function Dialog() {
+ return