1 | import React, { useContext } from 'react';
|
2 | import type { LoadedClerk } from '@clerk/types';
|
3 | import IsomorphicClerk from '../isomorphicClerk';
|
4 | import {
|
5 | assertClerkLoadedGuarantee,
|
6 | assertWrappedByClerkProvider,
|
7 | } from './assertHelpers';
|
8 | import {
|
9 | StructureContext,
|
10 | StructureContextStates,
|
11 | } from '../contexts/StructureContext';
|
12 | import { inBrowser } from '../utils';
|
13 | import { hocChildrenNotAFunctionError } from '../errors';
|
14 |
|
15 | type IsomorphicClerkContextValue = {
|
16 | value: IsomorphicClerk;
|
17 | };
|
18 | export const IsomorphicClerkContext = React.createContext<
|
19 | IsomorphicClerkContextValue | undefined
|
20 | >(undefined);
|
21 | IsomorphicClerkContext.displayName = 'IsomorphicClerkContext';
|
22 |
|
23 | export const useClerk = (): LoadedClerk => {
|
24 | const structureCtx = useContext(StructureContext);
|
25 | const clerkCtx = useContext(IsomorphicClerkContext);
|
26 | assertWrappedByClerkProvider(structureCtx);
|
27 | assertWrappedByClerkProvider(clerkCtx);
|
28 | assertClerkLoadedGuarantee(structureCtx.guaranteedLoaded, 'useClerk()');
|
29 | assertClerkLoadedGuarantee(clerkCtx.value, 'useClerk()');
|
30 |
|
31 |
|
32 | return (clerkCtx.value as unknown) as LoadedClerk;
|
33 | };
|
34 |
|
35 | export const withClerk = <P extends { clerk: LoadedClerk }>(
|
36 | Component: React.ComponentType<P>,
|
37 | displayName?: string,
|
38 | ) => {
|
39 | displayName =
|
40 | displayName || Component.displayName || Component.name || 'Component';
|
41 | Component.displayName = displayName;
|
42 | const HOC = (props: Omit<P, 'clerk'>) => {
|
43 | const structureCtx = useContext(StructureContext);
|
44 | const clerkCtx = useContext(IsomorphicClerkContext);
|
45 |
|
46 | if (!inBrowser()) {
|
47 | return null;
|
48 | }
|
49 |
|
50 | assertWrappedByClerkProvider(structureCtx);
|
51 | assertWrappedByClerkProvider(clerkCtx);
|
52 |
|
53 | const clerk = (clerkCtx.value as unknown) as LoadedClerk;
|
54 | if (!clerk) {
|
55 | return null;
|
56 | }
|
57 |
|
58 | if (structureCtx.guaranteedLoaded) {
|
59 | return <Component {...(props as P)} clerk={clerk} />;
|
60 | }
|
61 |
|
62 | if (clerk.client) {
|
63 | return (
|
64 | <StructureContext.Provider
|
65 | value={StructureContextStates.guaranteedLoaded}
|
66 | >
|
67 | <Component {...(props as P)} clerk={clerk} />
|
68 | </StructureContext.Provider>
|
69 | );
|
70 | }
|
71 |
|
72 | return null;
|
73 | };
|
74 | HOC.displayName = `withClerk(${displayName})`;
|
75 | return HOC;
|
76 | };
|
77 |
|
78 | export const WithClerk: React.FC<{
|
79 | children: (clerk: LoadedClerk) => React.ReactNode;
|
80 | }> = ({ children }) => (
|
81 | <StructureContext.Consumer>
|
82 | {structureCtx => (
|
83 | <IsomorphicClerkContext.Consumer>
|
84 | {clerkCtx => {
|
85 | if (typeof children !== 'function') {
|
86 | throw new Error(hocChildrenNotAFunctionError);
|
87 | }
|
88 |
|
89 | assertWrappedByClerkProvider(structureCtx);
|
90 | assertWrappedByClerkProvider(clerkCtx);
|
91 |
|
92 | const clerk = (clerkCtx.value as unknown) as LoadedClerk;
|
93 | if (!clerk) {
|
94 | return null;
|
95 | }
|
96 |
|
97 | if (structureCtx.guaranteedLoaded) {
|
98 | return children(clerk);
|
99 | }
|
100 |
|
101 | if (clerk.client) {
|
102 | return (
|
103 | <StructureContext.Provider
|
104 | value={StructureContextStates.guaranteedLoaded}
|
105 | >
|
106 | {children(clerk)}
|
107 | </StructureContext.Provider>
|
108 | );
|
109 | }
|
110 |
|
111 | return null;
|
112 | }}
|
113 | </IsomorphicClerkContext.Consumer>
|
114 | )}
|
115 | </StructureContext.Consumer>
|
116 | );
|