Skip to content

Commit 26b9378

Browse files
committed
fix: trigger should not render portal in ssr
1 parent d5ca99a commit 26b9378

File tree

4 files changed

+60
-3
lines changed

4 files changed

+60
-3
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@
4545
"@testing-library/react": "^13.0.0",
4646
"@types/classnames": "^2.2.10",
4747
"@types/jest": "^26.0.15",
48-
"@types/react": "^16.8.19",
49-
"@types/react-dom": "^16.8.4",
48+
"@types/react": "^18.0.0",
49+
"@types/react-dom": "^18.0.0",
5050
"cross-env": "^7.0.1",
5151
"dumi": "^2.1.0",
5252
"eslint": "^7.0.0",

src/Popup/ServerPortal.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
* Portal will not work in SSR. We need wrap this to not to break.
3+
*/
4+
export default function ServerPortal(): React.ReactElement {
5+
return null;
6+
}

src/Popup/index.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import type { CSSMotionProps } from 'rc-motion';
33
import CSSMotion from 'rc-motion';
44
import ResizeObserver from 'rc-resize-observer';
55
import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
6+
import canUseDom from 'rc-util/lib/Dom/canUseDom';
67
import * as React from 'react';
78
import type { TriggerProps } from '../';
89
import type { AlignType } from '../interface';
910
import Arrow from './Arrow';
1011
import Mask from './Mask';
12+
import ServerPortal from './ServerPortal';
1113

1214
export interface PopupProps {
1315
prefixCls: string;
@@ -93,7 +95,7 @@ const Popup = React.forwardRef<HTMLDivElement, PopupProps>((props, ref) => {
9395
forceRender,
9496
getPopupContainer,
9597
autoDestroy,
96-
portal: Portal,
98+
portal,
9799

98100
zIndex,
99101

@@ -166,6 +168,8 @@ const Popup = React.forwardRef<HTMLDivElement, PopupProps>((props, ref) => {
166168
miscStyle.pointerEvents = 'none';
167169
}
168170

171+
const Portal = canUseDom() ? portal: ServerPortal;
172+
169173
return (
170174
<Portal
171175
open={forceRender || isNodeVisible}

tests/ssr.test.tsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { cleanup } from '@testing-library/react';
2+
import { hydrateRoot } from 'react-dom/client';
3+
import { renderToString } from 'react-dom/server';
4+
import Trigger from '../src';
5+
6+
global.canUseDOM = false;
7+
8+
jest.mock('rc-util/lib/Dom/canUseDom', () => () => global.canUseDOM);
9+
10+
describe('Trigger.SSR', () => {
11+
beforeEach(() => {
12+
global.canUseDOM = false;
13+
jest.useFakeTimers();
14+
});
15+
16+
afterEach(() => {
17+
cleanup();
18+
jest.useRealTimers();
19+
});
20+
21+
it('normal', () => {
22+
const str = renderToString(
23+
<Trigger popupVisible popup={<strong>trigger</strong>} arrow>
24+
<div />
25+
</Trigger>,
26+
);
27+
expect(str).toBeTruthy();
28+
});
29+
30+
it('default visible should not block render', () => {
31+
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
32+
const node = (
33+
<Trigger popupVisible popup={<strong>trigger</strong>} arrow>
34+
<div />
35+
</Trigger>
36+
);
37+
const str = renderToString(node);
38+
expect(str).toBeTruthy();
39+
40+
console.log(str);
41+
42+
const div = document.createElement('div');
43+
hydrateRoot(div, node);
44+
45+
expect(errSpy).not.toHaveBeenCalled();
46+
});
47+
});

0 commit comments

Comments
 (0)