-
Notifications
You must be signed in to change notification settings - Fork 325
/
Copy pathutils.tsx
78 lines (70 loc) · 2.38 KB
/
utils.tsx
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
import type { LoadedClerk } from '@clerk/types';
import { computed, type Store, type StoreValue } from 'nanostores';
import React from 'react';
import { $clerk, $csrState } from '../stores/internal';
/**
* This implementation of `useStore` is an alternative solution to the hook exported by nanostores
* Reference: https://github.com/nanostores/react/blob/main/index.js
*/
function useStore<T extends Store, SV extends StoreValue<T>>(store: T): SV {
const get = store.get.bind(store);
return React.useSyncExternalStore(store.listen, get, get);
}
export const withClerk = <P extends { clerk: LoadedClerk | undefined | null }>(
Component: React.ComponentType<P>,
displayName?: string,
) => {
displayName = displayName || Component.displayName || Component.name || 'Component';
Component.displayName = displayName;
const HOC = (props: Omit<P, 'clerk'>) => {
const clerk = useStore(
computed([$csrState, $clerk], (state, clerk) => {
return state.isLoaded ? clerk : null;
}),
);
return (
<Component
/**
* Force the remount of the component if clerk is not loaded yet.
* This is needed in order to avoid hydration errors in controlComponents.
*/
key={clerk ? 'a' : 'b'}
{...(props as P)}
clerk={clerk}
/>
);
};
HOC.displayName = `withClerk(${displayName})`;
return HOC;
};
export type WithClerkProp<T = unknown> = T & {
clerk: LoadedClerk | undefined | null;
};
// TODO-SHARED: Duplicate from @clerk/clerk-react
export const assertSingleChild =
(children: React.ReactNode) =>
(name: 'SignInButton' | 'SignUpButton' | 'SignOutButton' | 'SignInWithMetamaskButton') => {
try {
return React.Children.only(children);
} catch {
return `You've passed multiple children components to <${name}/>. You can only pass a single child component or text.`;
}
};
// TODO-SHARED: Duplicate from @clerk/clerk-react
export const normalizeWithDefaultValue = (children: React.ReactNode | undefined, defaultText: string) => {
if (!children) {
children = defaultText;
}
if (typeof children === 'string') {
children = <button type='button'>{children}</button>;
}
return children;
};
// TODO-SHARED: Duplicate from @clerk/clerk-react
export const safeExecute =
(cb: unknown) =>
(...args: any) => {
if (cb && typeof cb === 'function') {
return cb(...args);
}
};