13
13
14
14
var ReactNative = require ( 'ReactNative' ) ;
15
15
var ReactNativeAttributePayload = require ( 'ReactNativeAttributePayload' ) ;
16
+ var ReactNativeFeatureFlags = require ( 'ReactNativeFeatureFlags' ) ;
16
17
var TextInputState = require ( 'TextInputState' ) ;
17
18
var UIManager = require ( 'UIManager' ) ;
18
19
19
20
var invariant = require ( 'fbjs/lib/invariant' ) ;
21
+ var findNodeHandle = require ( 'findNodeHandle' ) ;
20
22
21
- type MeasureOnSuccessCallback = (
22
- x : number ,
23
- y : number ,
24
- width : number ,
25
- height : number ,
26
- pageX : number ,
27
- pageY : number
28
- ) => void
29
-
30
- type MeasureInWindowOnSuccessCallback = (
31
- x : number ,
32
- y : number ,
33
- width : number ,
34
- height : number ,
35
- ) => void
36
-
37
- type MeasureLayoutOnSuccessCallback = (
38
- left : number ,
39
- top : number ,
40
- width : number ,
41
- height : number
42
- ) => void
43
-
44
- function warnForStyleProps ( props , validAttributes ) {
45
- for ( var key in validAttributes . style ) {
46
- if ( ! ( validAttributes [ key ] || props [ key ] === undefined ) ) {
47
- console . error (
48
- 'You are setting the style `{ ' + key + ': ... }` as a prop. You ' +
49
- 'should nest it in a style object. ' +
50
- 'E.g. `{ style: { ' + key + ': ... } }`'
51
- ) ;
52
- }
53
- }
54
- }
23
+ var {
24
+ mountSafeCallback,
25
+ throwOnStylesProp,
26
+ warnForStyleProps,
27
+ } = require ( 'NativeMethodsMixinUtils' ) ;
28
+
29
+ import type {
30
+ MeasureInWindowOnSuccessCallback ,
31
+ MeasureLayoutOnSuccessCallback ,
32
+ MeasureOnSuccessCallback ,
33
+ } from 'NativeMethodsMixinUtils' ;
34
+ import type { ReactNativeBaseComponentViewConfig } from 'ReactNativeViewConfigRegistry' ;
55
35
56
36
/**
57
37
* `NativeMethodsMixin` provides methods to access the underlying native
@@ -65,6 +45,10 @@ function warnForStyleProps(props, validAttributes) {
65
45
* information, see [Direct
66
46
* Manipulation](docs/direct-manipulation.html).
67
47
*/
48
+ // TODO (bvaughn) Figure out how to use the NativeMethodsInterface type to-
49
+ // ensure that these mixins and ReactNativeFiberHostComponent stay in sync.
50
+ // Unfortunately, using it causes Flow to complain WRT createClass mixins:
51
+ // "call of method `createClass`. Expected an exact object instead of ..."
68
52
var NativeMethodsMixin = {
69
53
/**
70
54
* Determines the location on screen, width, and height of the given view and
@@ -140,20 +124,15 @@ var NativeMethodsMixin = {
140
124
* Manipulation](docs/direct-manipulation.html)).
141
125
*/
142
126
setNativeProps : function ( nativeProps : Object ) {
143
- if ( __DEV__ ) {
144
- warnForStyleProps( nativeProps , this . viewConfig . validAttributes) ;
145
- }
127
+ // Ensure ReactNative factory function has configured findNodeHandle.
128
+ // Requiring it won't execute the factory function until first referenced.
129
+ // It's possible for tests that use ReactTestRenderer to reach this point,
130
+ // Without having executed ReactNative.
131
+ // Defer the factory function until now to avoid a cycle with UIManager.
132
+ // TODO (bvaughn) Remove this once ReactNativeStack is dropped.
133
+ require ( 'ReactNative' ) ;
146
134
147
- var updatePayload = ReactNativeAttributePayload . create (
148
- nativeProps ,
149
- this . viewConfig . validAttributes
150
- ) ;
151
-
152
- UIManager . updateView (
153
- ( ReactNative . findNodeHandle ( this ) : any ) ,
154
- this . viewConfig . uiViewClassName ,
155
- updatePayload
156
- ) ;
135
+ injectedSetNativeProps( this , nativeProps) ;
157
136
} ,
158
137
159
138
/**
@@ -172,19 +151,116 @@ var NativeMethodsMixin = {
172
151
} ,
173
152
} ;
174
153
175
- function throwOnStylesProp ( component , props ) {
176
- if ( props . styles !== undefined ) {
177
- var owner = component . _owner || null ;
178
- var name = component . constructor . displayName ;
179
- var msg = '`styles` is not a supported property of `' + name + '`, did ' +
180
- 'you mean `style` (singular)?' ;
181
- if ( owner && owner . constructor && owner . constructor . displayName ) {
182
- msg += '\n\nCheck the `' + owner . constructor . displayName + '` parent ' +
183
- ' component.' ;
154
+ // TODO (bvaughn) Inline this once ReactNativeStack is dropped.
155
+ function setNativePropsFiber ( componentOrHandle : any , nativeProps : Object ) {
156
+ // Class components don't have viewConfig -> validateAttributes.
157
+ // Nor does it make sense to set native props on a non-native component.
158
+ // Instead, find the nearest host component and set props on it.
159
+ // Use findNodeHandle() rather than ReactNative.findNodeHandle() because
160
+ // We want the instance/wrapper (not the native tag).
161
+ let maybeInstance ;
162
+
163
+ // Fiber errors if findNodeHandle is called for an umounted component.
164
+ // Tests using ReactTestRenderer will trigger this case indirectly.
165
+ // Mimicking stack behavior, we should silently ignore this case.
166
+ // TODO Fix ReactTestRenderer so we can remove this try/catch.
167
+ try {
168
+ maybeInstance = findNodeHandle ( componentOrHandle ) ;
169
+ } catch ( error ) { }
170
+
171
+ // If there is no host component beneath this we should fail silently.
172
+ // This is not an error; it could mean a class component rendered null.
173
+ if ( maybeInstance == null ) {
174
+ return ;
175
+ }
176
+
177
+ const viewConfig : ReactNativeBaseComponentViewConfig =
178
+ maybeInstance . viewConfig ;
179
+
180
+ if ( __DEV__ ) {
181
+ warnForStyleProps ( nativeProps , viewConfig . validAttributes ) ;
182
+ }
183
+
184
+ var updatePayload = ReactNativeAttributePayload . create (
185
+ nativeProps ,
186
+ viewConfig . validAttributes ,
187
+ ) ;
188
+
189
+ UIManager . updateView (
190
+ maybeInstance . _nativeTag ,
191
+ viewConfig . uiViewClassName ,
192
+ updatePayload ,
193
+ ) ;
194
+ }
195
+
196
+ // TODO (bvaughn) Remove this once ReactNativeStack is dropped.
197
+ function setNativePropsStack ( componentOrHandle : any , nativeProps : Object ) {
198
+ // Class components don't have viewConfig -> validateAttributes.
199
+ // Nor does it make sense to set native props on a non-native component.
200
+ // Instead, find the nearest host component and set props on it.
201
+ // Use findNodeHandle() rather than ReactNative.findNodeHandle() because
202
+ // We want the instance/wrapper (not the native tag).
203
+ let maybeInstance = findNodeHandle ( componentOrHandle ) ;
204
+
205
+ // If there is no host component beneath this we should fail silently.
206
+ // This is not an error; it could mean a class component rendered null.
207
+ if ( maybeInstance == null ) {
208
+ return ;
209
+ }
210
+
211
+ let viewConfig : ReactNativeBaseComponentViewConfig ;
212
+ if ( maybeInstance . viewConfig !== undefined ) {
213
+ // ReactNativeBaseComponent
214
+ viewConfig = maybeInstance . viewConfig ;
215
+ } else if (
216
+ maybeInstance . _instance !== undefined &&
217
+ maybeInstance . _instance . viewConfig !== undefined
218
+ ) {
219
+ // ReactCompositeComponentWrapper
220
+ // Some instances (eg Text) define their own viewConfig
221
+ viewConfig = maybeInstance . _instance . viewConfig ;
222
+ } else {
223
+ // ReactCompositeComponentWrapper
224
+ // Other instances (eg TextInput) defer to their children's viewConfig
225
+ while ( maybeInstance . _renderedComponent !== undefined ) {
226
+ maybeInstance = maybeInstance . _renderedComponent ;
184
227
}
185
- throw new Error ( msg ) ;
228
+ viewConfig = maybeInstance . viewConfig ;
229
+ }
230
+
231
+ const tag : number = typeof maybeInstance . getHostNode === 'function'
232
+ ? maybeInstance . getHostNode ( )
233
+ : maybeInstance . _rootNodeID ;
234
+
235
+ if ( __DEV__ ) {
236
+ warnForStyleProps ( nativeProps , viewConfig . validAttributes ) ;
186
237
}
238
+
239
+ var updatePayload = ReactNativeAttributePayload . create (
240
+ nativeProps ,
241
+ viewConfig . validAttributes ,
242
+ ) ;
243
+
244
+ UIManager . updateView (
245
+ tag ,
246
+ viewConfig . uiViewClassName ,
247
+ updatePayload ,
248
+ ) ;
249
+ }
250
+
251
+ // Switching based on fiber vs stack to avoid a lot of inline checks at runtime.
252
+ // HACK Normally this injection would be done by the renderer, but in this case
253
+ // that would result in a cycle between ReactNative and NativeMethodsMixin.
254
+ // We avoid requiring additional code for this injection so it's probably ok?
255
+ // TODO (bvaughn) Remove this once ReactNativeStack is gone.
256
+ let injectedSetNativeProps :
257
+ ( componentOrHandle : any , nativeProps : Object ) => void ;
258
+ if ( ReactNativeFeatureFlags . useFiber ) {
259
+ injectedSetNativeProps = setNativePropsFiber ;
260
+ } else {
261
+ injectedSetNativeProps = setNativePropsStack ;
187
262
}
263
+
188
264
if ( __DEV__ ) {
189
265
// hide this from Flow since we can't define these properties outside of
190
266
// __DEV__ without actually implementing them (setting them to undefined
@@ -203,20 +279,4 @@ if (__DEV__) {
203
279
} ;
204
280
}
205
281
206
- /**
207
- * In the future, we should cleanup callbacks by cancelling them instead of
208
- * using this.
209
- */
210
- function mountSafeCallback (
211
- context : ReactComponent < any , any , any > ,
212
- callback : ?Function
213
- ) : any {
214
- return function ( ) {
215
- if ( ! callback || ( typeof context . isMounted === 'function' && ! context . isMounted ( ) ) ) {
216
- return undefined ;
217
- }
218
- return callback . apply ( context , arguments ) ;
219
- } ;
220
- }
221
-
222
282
module . exports = NativeMethodsMixin ;
0 commit comments