1 | import React from "react"
|
2 | import { observer } from "./observer"
|
3 | import { copyStaticProperties } from "./utils/utils"
|
4 | import { MobXProviderContext } from "./Provider"
|
5 | import { IReactComponent } from "./types/IReactComponent"
|
6 | import { IValueMap } from "./types/IValueMap"
|
7 | import { IWrappedComponent } from "./types/IWrappedComponent"
|
8 | import { IStoresToProps } from "./types/IStoresToProps"
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | function createStoreInjector(
|
14 | grabStoresFn: IStoresToProps,
|
15 | component: IReactComponent<any>,
|
16 | injectNames: string,
|
17 | makeReactive: boolean
|
18 | ): IReactComponent<any> {
|
19 |
|
20 | let Injector: IReactComponent<any> = React.forwardRef((props, ref) => {
|
21 | const newProps = { ...props }
|
22 | const context = React.useContext(MobXProviderContext)
|
23 | Object.assign(newProps, grabStoresFn(context || {}, newProps) || {})
|
24 |
|
25 | if (ref) {
|
26 | newProps.ref = ref
|
27 | }
|
28 |
|
29 | return React.createElement(component, newProps)
|
30 | })
|
31 |
|
32 | if (makeReactive) Injector = observer(Injector)
|
33 | Injector["isMobxInjector"] = true
|
34 |
|
35 |
|
36 | copyStaticProperties(component, Injector)
|
37 | Injector["wrappedComponent"] = component
|
38 | Injector.displayName = getInjectName(component, injectNames)
|
39 | return Injector
|
40 | }
|
41 |
|
42 | function getInjectName(component: IReactComponent<any>, injectNames: string): string {
|
43 | let displayName
|
44 | const componentName =
|
45 | component.displayName ||
|
46 | component.name ||
|
47 | (component.constructor && component.constructor.name) ||
|
48 | "Component"
|
49 | if (injectNames) displayName = "inject-with-" + injectNames + "(" + componentName + ")"
|
50 | else displayName = "inject(" + componentName + ")"
|
51 | return displayName
|
52 | }
|
53 |
|
54 | function grabStoresByName(
|
55 | storeNames: Array<string>
|
56 | ): (baseStores: IValueMap, nextProps: React.Props<any>) => React.PropsWithRef<any> | undefined {
|
57 | return function (baseStores, nextProps) {
|
58 | storeNames.forEach(function (storeName) {
|
59 | if (
|
60 | storeName in nextProps
|
61 | )
|
62 | return
|
63 | if (!(storeName in baseStores))
|
64 | throw new Error(
|
65 | "MobX injector: Store '" +
|
66 | storeName +
|
67 | "' is not available! Make sure it is provided by some Provider"
|
68 | )
|
69 | nextProps[storeName] = baseStores[storeName]
|
70 | })
|
71 | return nextProps
|
72 | }
|
73 | }
|
74 |
|
75 | export function inject(
|
76 | ...stores: Array<string>
|
77 | ): <T extends IReactComponent<any>>(
|
78 | target: T
|
79 | ) => T & (T extends IReactComponent<infer P> ? IWrappedComponent<P> : never)
|
80 | export function inject<S, P, I, C>(
|
81 | fn: IStoresToProps<S, P, I, C>
|
82 | ): <T extends IReactComponent>(target: T) => T & IWrappedComponent<P>
|
83 |
|
84 | /**
|
85 | * higher order component that injects stores to a child.
|
86 | * takes either a varargs list of strings, which are stores read from the context,
|
87 | * or a function that manually maps the available stores from the context to props:
|
88 | * storesToProps(mobxStores, props, context) => newProps
|
89 | */
|
90 | export function inject( ...storeNames: Array<any>) {
|
91 | if (typeof arguments[0] === "function") {
|
92 | let grabStoresFn = arguments[0]
|
93 | return (componentClass: React.ComponentClass<any, any>) =>
|
94 | createStoreInjector(grabStoresFn, componentClass, grabStoresFn.name, true)
|
95 | } else {
|
96 | return (componentClass: React.ComponentClass<any, any>) =>
|
97 | createStoreInjector(
|
98 | grabStoresByName(storeNames),
|
99 | componentClass,
|
100 | storeNames.join("-"),
|
101 | false
|
102 | )
|
103 | }
|
104 | }
|
105 |
|
\ | No newline at end of file |