UNPKG

6.98 kBJavaScriptView Raw
1'use strict';
2
3exports.__esModule = true;
4
5var _react = require('react');
6
7var _transferStaticProps = require('./transferStaticProps');
8
9var _transferStaticProps2 = _interopRequireDefault(_transferStaticProps);
10
11var _constants = require('./constants');
12
13var _utils = require('./utils');
14
15var _inject = require('./inject');
16
17function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
18
19var has = Object.prototype.hasOwnProperty;
20
21var proxies = new WeakMap();
22
23var defineClassMember = function defineClassMember(Class, methodName, methodBody) {
24 return (0, _utils.safeDefineProperty)(Class.prototype, methodName, {
25 configurable: true,
26 writable: true,
27 enumerable: false,
28 value: methodBody
29 });
30};
31
32function createClassProxy(InitialComponent, proxyKey) {
33 var wrapResult = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _utils.identity;
34
35 // Prevent double wrapping.
36 // Given a proxy class, return the existing proxy managing it.
37 var existingProxy = proxies.get(InitialComponent);
38
39 if (existingProxy) {
40 return existingProxy;
41 }
42
43 var CurrentComponent = void 0;
44 var savedDescriptors = {};
45 var injectedMembers = {};
46 var proxyGeneration = 0;
47 var isFunctionalComponent = !(0, _utils.isReactClass)(InitialComponent);
48
49 var lastInstance = null;
50
51 function postConstructionAction() {
52 this[_constants.GENERATION] = 0;
53
54 // As long we can't override constructor
55 // every class shall evolve from a base class
56 (0, _inject.inject)(this, proxyGeneration, injectedMembers);
57
58 lastInstance = this;
59 }
60
61 function proxiedUpdate() {
62 (0, _inject.inject)(this, proxyGeneration, injectedMembers);
63 }
64
65 function lifeCycleWrapperFactory(wrapperName) {
66 return function wrappedMethod() {
67 proxiedUpdate.call(this);
68
69 for (var _len = arguments.length, rest = Array(_len), _key = 0; _key < _len; _key++) {
70 rest[_key] = arguments[_key];
71 }
72
73 return !isFunctionalComponent && CurrentComponent.prototype[wrapperName] && CurrentComponent.prototype[wrapperName].apply(this, rest);
74 };
75 }
76
77 var componentWillReceiveProps = lifeCycleWrapperFactory('componentWillReceiveProps');
78 var componentWillUpdate = lifeCycleWrapperFactory('componentWillUpdate');
79
80 function proxiedRender() {
81 proxiedUpdate.call(this);
82
83 var result = void 0;
84
85 // We need to use hasOwnProperty here, as the cached result is a React node
86 // and can be null or some other falsy value.
87 if (has.call(this, _constants.CACHED_RESULT)) {
88 result = this[_constants.CACHED_RESULT];
89 delete this[_constants.CACHED_RESULT];
90 } else if (isFunctionalComponent) {
91 result = CurrentComponent(this.props, this.context);
92 } else {
93 result = CurrentComponent.prototype.render.call(this);
94 }
95
96 return wrapResult(result);
97 }
98
99 var defineProxyMethods = function defineProxyMethods(Proxy) {
100 defineClassMember(Proxy, 'render', proxiedRender);
101 defineClassMember(Proxy, 'componentWillReceiveProps', componentWillReceiveProps);
102 defineClassMember(Proxy, 'componentWillUpdate', componentWillUpdate);
103 };
104
105 var ProxyFacade = void 0;
106 var ProxyComponent = null;
107
108 if (!isFunctionalComponent) {
109 ProxyComponent = (0, _utils.proxyClassCreator)(InitialComponent, postConstructionAction);
110
111 defineProxyMethods(ProxyComponent);
112
113 ProxyFacade = ProxyComponent;
114 } else {
115 // This function only gets called for the initial mount. The actual
116 // rendered component instance will be the return value.
117
118 // eslint-disable-next-line func-names
119 ProxyFacade = function ProxyFacade(props, context) {
120 var result = CurrentComponent(props, context);
121
122 // This is a Relay-style container constructor. We can't do the prototype-
123 // style wrapping for this as we do elsewhere, so just we just pass it
124 // through as-is.
125 if ((0, _utils.isReactComponentInstance)(result)) {
126 ProxyComponent = null;
127 return result;
128 }
129
130 // Otherwise, it's a normal functional component. Build the real proxy
131 // and use it going forward.
132 ProxyComponent = (0, _utils.proxyClassCreator)(_react.Component, postConstructionAction);
133
134 defineProxyMethods(ProxyComponent);
135
136 var determinateResult = new ProxyComponent(props, context);
137
138 // Cache the initial render result so we don't call the component function
139 // a second time for the initial render.
140 determinateResult[_constants.CACHED_RESULT] = result;
141 return determinateResult;
142 };
143 }
144
145 function get() {
146 return ProxyFacade;
147 }
148
149 function getCurrent() {
150 return CurrentComponent;
151 }
152
153 (0, _utils.safeDefineProperty)(ProxyFacade, _constants.UNWRAP_PROXY, {
154 configurable: false,
155 writable: false,
156 enumerable: false,
157 value: getCurrent
158 });
159
160 (0, _utils.safeDefineProperty)(ProxyFacade, _constants.PROXY_KEY, {
161 configurable: false,
162 writable: false,
163 enumerable: false,
164 value: proxyKey
165 });
166
167 (0, _utils.safeDefineProperty)(ProxyFacade, 'toString', {
168 configurable: true,
169 writable: false,
170 enumerable: false,
171 value: function toString() {
172 return String(CurrentComponent);
173 }
174 });
175
176 function update(NextComponent) {
177 if (typeof NextComponent !== 'function') {
178 throw new Error('Expected a constructor.');
179 }
180
181 if (NextComponent === CurrentComponent) {
182 return;
183 }
184
185 // Prevent proxy cycles
186 var existingProxy = proxies.get(NextComponent);
187 if (existingProxy) {
188 update(existingProxy[_constants.UNWRAP_PROXY]());
189 return;
190 }
191
192 isFunctionalComponent = !(0, _utils.isReactClass)(NextComponent);
193 proxyGeneration++;
194
195 // Save the next constructor so we call it
196 var PreviousComponent = CurrentComponent;
197 CurrentComponent = NextComponent;
198
199 // Try to infer displayName
200 var displayName = (0, _utils.getDisplayName)(CurrentComponent);
201 ProxyFacade.displayName = displayName;
202
203 if (ProxyComponent) {
204 (0, _utils.safeDefineProperty)(ProxyComponent, 'name', {
205 value: displayName
206 });
207 }
208
209 savedDescriptors = (0, _transferStaticProps2.default)(ProxyFacade, savedDescriptors, PreviousComponent, NextComponent);
210
211 if (isFunctionalComponent || !ProxyComponent) {
212 // nothing
213 } else {
214 (0, _inject.checkLifeCycleMethods)(ProxyComponent, NextComponent);
215 Object.setPrototypeOf(ProxyComponent.prototype, NextComponent.prototype);
216 if (proxyGeneration > 1) {
217 injectedMembers = (0, _inject.mergeComponents)(ProxyComponent, NextComponent, InitialComponent, lastInstance, injectedMembers);
218 }
219 }
220 }
221
222 update(InitialComponent);
223
224 var proxy = { get: get, update: update };
225 proxies.set(ProxyFacade, proxy);
226
227 (0, _utils.safeDefineProperty)(proxy, _constants.UNWRAP_PROXY, {
228 configurable: false,
229 writable: false,
230 enumerable: false,
231 value: getCurrent
232 });
233
234 return proxy;
235}
236
237exports.default = createClassProxy;
\No newline at end of file