Skip to content

Commit e64f4f1

Browse files
authored
fix: not queue when already created (#10)
* docs: add order demo for debug * fix: portal create not block * refactor: reorder * test: order test
1 parent 85e6e15 commit e64f4f1

File tree

4 files changed

+122
-6
lines changed

4 files changed

+122
-6
lines changed

docs/demo/inlineOrder.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
## Inline Order
2+
3+
4+
<code src="../examples/inlineOrder.tsx" />

docs/examples/inlineOrder.tsx

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import React, { version } from 'react';
2+
import Portal from '../../src';
3+
import './basic.less';
4+
5+
const Child = () => {
6+
const divRef = React.useRef<HTMLPreElement>(null);
7+
8+
React.useEffect(() => {
9+
const path: Element[] = [];
10+
11+
for (let cur: HTMLElement = divRef.current; cur; cur = cur.parentElement) {
12+
path.push(cur);
13+
}
14+
15+
console.log('Path:', path);
16+
}, []);
17+
18+
return (
19+
<pre ref={divRef} style={{ border: '1px solid red' }}>
20+
<p>Hello Child {version}</p>
21+
</pre>
22+
);
23+
};
24+
25+
export default () => {
26+
const [show1, setShow1] = React.useState(false);
27+
const [show2, setShow2] = React.useState(false);
28+
29+
return (
30+
<React.StrictMode>
31+
<button
32+
onClick={() => {
33+
setShow1(!show1);
34+
}}
35+
>
36+
Trigger Inner Child
37+
</button>
38+
<button
39+
onClick={() => {
40+
setShow2(!show2);
41+
}}
42+
>
43+
Trigger Outer Child
44+
</button>
45+
46+
<Portal open>
47+
<div style={{ border: '1px solid red' }}>
48+
<p>Hello Root {version}</p>
49+
50+
{show1 && (
51+
<Portal open>
52+
<Child />
53+
</Portal>
54+
)}
55+
</div>
56+
</Portal>
57+
58+
{show2 && (
59+
<Portal open>
60+
<Child />
61+
</Portal>
62+
)}
63+
</React.StrictMode>
64+
);
65+
};

src/useDom.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,35 @@ export default function useDom(
2929
});
3030

3131
// ========================== Order ==========================
32+
const appendedRef = React.useRef(false);
33+
3234
const queueCreate = React.useContext(OrderContext);
3335
const [queue, setQueue] = React.useState<VoidFunction[]>(EMPTY_LIST);
3436

3537
const mergedQueueCreate =
3638
queueCreate ||
37-
((appendFn: VoidFunction) => {
38-
setQueue(origin => {
39-
const newQueue = [appendFn, ...origin];
40-
return newQueue;
41-
});
42-
});
39+
(appendedRef.current
40+
? undefined
41+
: (appendFn: VoidFunction) => {
42+
setQueue(origin => {
43+
const newQueue = [appendFn, ...origin];
44+
return newQueue;
45+
});
46+
});
4347

4448
// =========================== DOM ===========================
4549
function append() {
4650
if (!ele.parentElement) {
4751
document.body.appendChild(ele);
4852
}
53+
54+
appendedRef.current = true;
4955
}
5056

5157
function cleanup() {
5258
ele.parentElement?.removeChild(ele);
59+
60+
appendedRef.current = false;
5361
}
5462

5563
useLayoutEffect(() => {

tests/index.test.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ jest.mock('../src/util', () => {
1212
};
1313
});
1414

15+
// Revert `useLayoutEffect` back to real one since we should keep order for test
16+
jest.mock('rc-util/lib/hooks/useLayoutEffect', () => {
17+
const origin = jest.requireActual('react');
18+
return origin.useLayoutEffect;
19+
});
20+
1521
describe('Portal', () => {
1622
beforeEach(() => {
1723
global.isOverflow = true;
@@ -249,4 +255,37 @@ describe('Portal', () => {
249255
rerender(<Demo open />);
250256
expect(checked).toBeTruthy();
251257
});
258+
259+
it('not block if parent already created', () => {
260+
const Checker = () => {
261+
const divRef = React.useRef<HTMLDivElement>(null);
262+
const [inDoc, setInDoc] = React.useState(false);
263+
264+
React.useEffect(() => {
265+
setInDoc(document.contains(divRef.current));
266+
}, []);
267+
268+
return (
269+
<div ref={divRef} className="checker">
270+
{String(inDoc)}
271+
</div>
272+
);
273+
};
274+
275+
const Demo = ({ visible }: any) => (
276+
<Portal open>
277+
{visible && (
278+
<Portal open>
279+
<Checker />
280+
</Portal>
281+
)}
282+
</Portal>
283+
);
284+
285+
const { rerender } = render(<Demo />);
286+
287+
rerender(<Demo visible />);
288+
289+
expect(document.querySelector('.checker').textContent).toEqual('true');
290+
});
252291
});

0 commit comments

Comments
 (0)