1 | import { forwardRef, memo } from "react"
|
2 |
|
3 | import { isUsingStaticRendering } from "./staticRendering"
|
4 | import { useObserver } from "./useObserver"
|
5 |
|
6 | export interface IObserverOptions {
|
7 | readonly forwardRef?: boolean
|
8 | }
|
9 |
|
10 | export function observer<P extends object, TRef = {}>(
|
11 | baseComponent: React.RefForwardingComponent<TRef, P>,
|
12 | options: IObserverOptions & { forwardRef: true }
|
13 | ): React.MemoExoticComponent<
|
14 | React.ForwardRefExoticComponent<React.PropsWithoutRef<P> & React.RefAttributes<TRef>>
|
15 | >
|
16 |
|
17 | export function observer<P extends object>(
|
18 | baseComponent: React.FunctionComponent<P>,
|
19 | options?: IObserverOptions
|
20 | ): React.FunctionComponent<P>
|
21 |
|
22 | export function observer<
|
23 | C extends React.FunctionComponent<any> | React.RefForwardingComponent<any>,
|
24 | Options extends IObserverOptions
|
25 | >(
|
26 | baseComponent: C,
|
27 | options?: Options
|
28 | ): Options extends { forwardRef: true }
|
29 | ? C extends React.RefForwardingComponent<infer TRef, infer P>
|
30 | ? C &
|
31 | React.MemoExoticComponent<
|
32 | React.ForwardRefExoticComponent<
|
33 | React.PropsWithoutRef<P> & React.RefAttributes<TRef>
|
34 | >
|
35 | >
|
36 | : never /* forwardRef set for a non forwarding component */
|
37 | : C & { displayName: string }
|
38 |
|
39 |
|
40 | export function observer<P extends object, TRef = {}>(
|
41 | baseComponent: React.RefForwardingComponent<TRef, P> | React.FunctionComponent<P>,
|
42 | options?: IObserverOptions
|
43 | ) {
|
44 |
|
45 | if (isUsingStaticRendering()) {
|
46 | return baseComponent
|
47 | }
|
48 |
|
49 | const realOptions = {
|
50 | forwardRef: false,
|
51 | ...options
|
52 | }
|
53 |
|
54 | const baseComponentName = baseComponent.displayName || baseComponent.name
|
55 |
|
56 | const wrappedComponent = (props: P, ref: React.Ref<TRef>) => {
|
57 | return useObserver(() => baseComponent(props, ref), baseComponentName)
|
58 | }
|
59 | wrappedComponent.displayName = baseComponentName
|
60 |
|
61 |
|
62 |
|
63 |
|
64 | let memoComponent
|
65 | if (realOptions.forwardRef) {
|
66 |
|
67 |
|
68 |
|
69 |
|
70 | memoComponent = memo(forwardRef(wrappedComponent))
|
71 | } else {
|
72 | memoComponent = memo(wrappedComponent)
|
73 | }
|
74 |
|
75 | copyStaticProperties(baseComponent, memoComponent)
|
76 | memoComponent.displayName = baseComponentName
|
77 |
|
78 | return memoComponent
|
79 | }
|
80 |
|
81 |
|
82 | const hoistBlackList: any = {
|
83 | $$typeof: true,
|
84 | render: true,
|
85 | compare: true,
|
86 | type: true
|
87 | }
|
88 |
|
89 | function copyStaticProperties(base: any, target: any) {
|
90 | Object.keys(base).forEach(key => {
|
91 | if (!hoistBlackList[key]) {
|
92 | Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(base, key)!)
|
93 | }
|
94 | })
|
95 | }
|