-
Notifications
You must be signed in to change notification settings - Fork 441
Expand file tree
/
Copy pathClerkHostRenderer.tsx
More file actions
119 lines (100 loc) · 3.82 KB
/
ClerkHostRenderer.tsx
File metadata and controls
119 lines (100 loc) · 3.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { without } from '@clerk/shared/object';
import { isDeeplyEqual } from '@clerk/shared/react';
import type { PropsWithChildren } from 'react';
import React from 'react';
import type { MountProps, OpenProps } from '../types';
const isMountProps = (props: any): props is MountProps => {
return 'mount' in props;
};
const isOpenProps = (props: any): props is OpenProps => {
return 'open' in props;
};
// README: <ClerkHostRenderer/> should be a class pure component in order for mount and unmount
// lifecycle props to be invoked correctly. Replacing the class component with a
// functional component wrapped with a React.memo is not identical to the original
// class implementation due to React intricacies such as the useEffect’s cleanup
// seems to run AFTER unmount, while componentWillUnmount runs BEFORE.
// More information can be found at https://clerk.slack.com/archives/C015S0BGH8R/p1624891993016300
// The function Portal implementation is commented out for future reference.
// const Portal = React.memo(({ props, mount, unmount }: MountProps) => {
// const portalRef = React.createRef<HTMLDivElement>();
// useEffect(() => {
// if (portalRef.current) {
// mount(portalRef.current, props);
// }
// return () => {
// if (portalRef.current) {
// unmount(portalRef.current);
// }
// };
// }, []);
// return <div ref={portalRef} />;
// });
// Portal.displayName = 'ClerkPortal';
/**
* Used to orchestrate mounting of Clerk components in a host React application.
* Components are rendered into a specific DOM node using mount/unmount methods provided by the Clerk class.
*/
export class ClerkHostRenderer extends React.PureComponent<
PropsWithChildren<
(MountProps | OpenProps) & {
component?: string;
hideRootHtmlElement?: boolean;
rootProps?: JSX.IntrinsicElements['div'];
}
>
> {
private rootRef = React.createRef<HTMLDivElement>();
componentDidUpdate(_prevProps: Readonly<MountProps | OpenProps>) {
if (!isMountProps(_prevProps) || !isMountProps(this.props)) {
return;
}
// Remove children and customPages from props before comparing
// children might hold circular references which deepEqual can't handle
// and the implementation of customPages relies on props getting new references
const prevProps = without(_prevProps.props, 'customPages', 'children');
const newProps = without(this.props.props, 'customPages', 'children');
// instead, we simply use the length of customPages to determine if it changed or not
const customPagesChanged = prevProps.customPages?.length !== newProps.customPages?.length;
const customMenuItemsChanged = prevProps.customMenuItems?.length !== newProps.customMenuItems?.length;
if (!isDeeplyEqual(prevProps, newProps) || customPagesChanged || customMenuItemsChanged) {
if (this.rootRef.current) {
this.props.updateProps({ node: this.rootRef.current, props: this.props.props });
}
}
}
componentDidMount() {
if (this.rootRef.current) {
if (isMountProps(this.props)) {
this.props.mount(this.rootRef.current, this.props.props);
}
if (isOpenProps(this.props)) {
this.props.open(this.props.props);
}
}
}
componentWillUnmount() {
if (this.rootRef.current) {
if (isMountProps(this.props)) {
this.props.unmount(this.rootRef.current);
}
if (isOpenProps(this.props)) {
this.props.close();
}
}
}
render() {
const { hideRootHtmlElement = false } = this.props;
const rootAttributes = {
ref: this.rootRef,
...this.props.rootProps,
...(this.props.component && { 'data-clerk-component': this.props.component }),
};
return (
<>
{!hideRootHtmlElement && <div {...rootAttributes} />}
{this.props.children}
</>
);
}
}