0
# React Native Native Bridge APIs
1
2
React Native provides powerful APIs for bridging between JavaScript and native code, enabling access to platform-specific functionality and custom native modules.
3
4
## Installation
5
6
```bash
7
npm install react-native
8
```
9
10
## Native Module Access
11
12
### NativeModules
13
14
Access all registered native modules and their exposed methods from JavaScript.
15
16
```javascript { .api }
17
// ESM
18
import {NativeModules} from 'react-native';
19
20
// CommonJS
21
const {NativeModules} = require('react-native');
22
23
// Access built-in native modules
24
const {
25
StatusBarManager,
26
DeviceInfo,
27
NetworkingIOS,
28
PlatformConstants,
29
UIManager,
30
} = NativeModules;
31
32
// Example: Using StatusBarManager (iOS)
33
if (Platform.OS === 'ios' && StatusBarManager) {
34
StatusBarManager.getHeight((statusBarFrameData) => {
35
console.log('Status bar height:', statusBarFrameData.height);
36
});
37
}
38
39
// Access custom native modules
40
const {MyCustomModule} = NativeModules;
41
42
if (MyCustomModule) {
43
// Call native method
44
MyCustomModule.doSomething('parameter', (error, result) => {
45
if (error) {
46
console.error('Native module error:', error);
47
} else {
48
console.log('Native module result:', result);
49
}
50
});
51
52
// Promise-based native method
53
MyCustomModule.asyncOperation('data')
54
.then(result => console.log('Async result:', result))
55
.catch(error => console.error('Async error:', error));
56
}
57
58
// Check if native module exists
59
function useNativeModule(moduleName) {
60
const module = NativeModules[moduleName];
61
62
if (!module) {
63
console.warn(`Native module ${moduleName} is not available`);
64
return null;
65
}
66
67
return module;
68
}
69
70
// Safe native module usage
71
function SafeNativeModuleComponent() {
72
const customModule = useNativeModule('MyCustomModule');
73
74
const handleNativeCall = async () => {
75
if (!customModule) {
76
Alert.alert('Error', 'Native module not available');
77
return;
78
}
79
80
try {
81
const result = await customModule.performOperation();
82
console.log('Operation completed:', result);
83
} catch (error) {
84
console.error('Native operation failed:', error);
85
Alert.alert('Error', 'Operation failed');
86
}
87
};
88
89
return (
90
<Button
91
title="Call Native Module"
92
onPress={handleNativeCall}
93
disabled={!customModule}
94
/>
95
);
96
}
97
98
// Batch native operations
99
async function batchNativeOperations() {
100
const {FileManager, DatabaseModule} = NativeModules;
101
102
if (!FileManager || !DatabaseModule) {
103
throw new Error('Required native modules not available');
104
}
105
106
try {
107
// Parallel native operations
108
const [fileResult, dbResult] = await Promise.all([
109
FileManager.readFile('/path/to/file'),
110
DatabaseModule.query('SELECT * FROM users'),
111
]);
112
113
return {fileResult, dbResult};
114
} catch (error) {
115
console.error('Batch operations failed:', error);
116
throw error;
117
}
118
}
119
120
// Native module constants
121
function getNativeConstants() {
122
const constants = {};
123
124
Object.keys(NativeModules).forEach(moduleName => {
125
const module = NativeModules[moduleName];
126
if (module && module.getConstants) {
127
constants[moduleName] = module.getConstants();
128
}
129
});
130
131
return constants;
132
}
133
134
// Example custom native module interface
135
const CameraModule = NativeModules.CameraModule;
136
137
const cameraAPI = {
138
// Check permissions
139
hasPermission: () => CameraModule?.hasPermission() || Promise.resolve(false),
140
141
// Request permissions
142
requestPermission: () => CameraModule?.requestPermission() || Promise.reject('Not available'),
143
144
// Take photo
145
capturePhoto: (options = {}) => {
146
if (!CameraModule) {
147
return Promise.reject('Camera module not available');
148
}
149
150
return CameraModule.capturePhoto({
151
quality: 0.8,
152
format: 'jpeg',
153
...options,
154
});
155
},
156
157
// Record video
158
recordVideo: (options = {}) => {
159
if (!CameraModule) {
160
return Promise.reject('Camera module not available');
161
}
162
163
return CameraModule.recordVideo({
164
maxDuration: 60,
165
quality: 'high',
166
...options,
167
});
168
},
169
};
170
171
// Hook for native module
172
function useCameraModule() {
173
const [hasPermission, setHasPermission] = useState(false);
174
const [isLoading, setIsLoading] = useState(false);
175
176
useEffect(() => {
177
checkPermission();
178
}, []);
179
180
const checkPermission = async () => {
181
try {
182
const permission = await cameraAPI.hasPermission();
183
setHasPermission(permission);
184
} catch (error) {
185
console.error('Permission check failed:', error);
186
}
187
};
188
189
const requestPermission = async () => {
190
setIsLoading(true);
191
try {
192
const granted = await cameraAPI.requestPermission();
193
setHasPermission(granted);
194
return granted;
195
} catch (error) {
196
console.error('Permission request failed:', error);
197
return false;
198
} finally {
199
setIsLoading(false);
200
}
201
};
202
203
const capturePhoto = async (options) => {
204
if (!hasPermission) {
205
const granted = await requestPermission();
206
if (!granted) throw new Error('Camera permission required');
207
}
208
209
return cameraAPI.capturePhoto(options);
210
};
211
212
return {
213
hasPermission,
214
isLoading,
215
requestPermission,
216
capturePhoto,
217
recordVideo: cameraAPI.recordVideo,
218
};
219
}
220
```
221
222
```typescript { .api }
223
interface NativeModulesStatic {
224
[key: string]: any;
225
}
226
227
// Common native module patterns
228
interface NativeModuleBase {
229
getConstants?(): {[key: string]: any};
230
addListener?(eventType: string): void;
231
removeListeners?(count: number): void;
232
}
233
234
// Example native module interfaces
235
interface FileSystemModule extends NativeModuleBase {
236
readFile(path: string): Promise<string>;
237
writeFile(path: string, content: string): Promise<void>;
238
deleteFile(path: string): Promise<void>;
239
exists(path: string): Promise<boolean>;
240
mkdir(path: string): Promise<void>;
241
readdir(path: string): Promise<string[]>;
242
}
243
244
interface DatabaseModule extends NativeModuleBase {
245
open(path: string): Promise<void>;
246
close(): Promise<void>;
247
query(sql: string, params?: any[]): Promise<any[]>;
248
execute(sql: string, params?: any[]): Promise<void>;
249
}
250
251
interface BluetoothModule extends NativeModuleBase {
252
isEnabled(): Promise<boolean>;
253
enable(): Promise<void>;
254
scan(duration?: number): Promise<Device[]>;
255
connect(deviceId: string): Promise<void>;
256
disconnect(deviceId: string): Promise<void>;
257
sendData(data: string): Promise<void>;
258
}
259
```
260
261
### TurboModuleRegistry
262
263
Access the new TurboModule system for improved performance and type safety.
264
265
```javascript { .api }
266
import {TurboModuleRegistry} from 'react-native';
267
268
// Get TurboModule
269
const MyTurboModule = TurboModuleRegistry.get('MyTurboModule');
270
271
// TurboModule with type safety (requires native implementation)
272
function useTurboModule(moduleName) {
273
const [module, setModule] = useState(null);
274
275
useEffect(() => {
276
try {
277
const turboModule = TurboModuleRegistry.get(moduleName);
278
setModule(turboModule);
279
} catch (error) {
280
console.warn(`TurboModule ${moduleName} not found:`, error);
281
}
282
}, [moduleName]);
283
284
return module;
285
}
286
287
// Example TurboModule usage
288
function TurboModuleComponent() {
289
const calculator = useTurboModule('CalculatorTurboModule');
290
291
const [result, setResult] = useState(0);
292
293
const performCalculation = async () => {
294
if (!calculator) {
295
Alert.alert('Error', 'Calculator module not available');
296
return;
297
}
298
299
try {
300
// TurboModule methods are synchronous by default
301
const sum = calculator.add(10, 20);
302
const product = calculator.multiply(5, 4);
303
304
setResult(sum + product);
305
} catch (error) {
306
console.error('Calculation failed:', error);
307
}
308
};
309
310
return (
311
<View>
312
<Text>Result: {result}</Text>
313
<Button title="Calculate" onPress={performCalculation} />
314
</View>
315
);
316
}
317
318
// TurboModule with async operations
319
function AsyncTurboModuleExample() {
320
const networkModule = useTurboModule('NetworkTurboModule');
321
const [data, setData] = useState(null);
322
const [loading, setLoading] = useState(false);
323
324
const fetchData = async () => {
325
if (!networkModule) return;
326
327
setLoading(true);
328
try {
329
// Async TurboModule method
330
const response = await networkModule.fetchData('https://api.example.com/data');
331
setData(response);
332
} catch (error) {
333
console.error('Fetch failed:', error);
334
} finally {
335
setLoading(false);
336
}
337
};
338
339
return (
340
<View>
341
{loading && <ActivityIndicator />}
342
{data && <Text>{JSON.stringify(data, null, 2)}</Text>}
343
<Button title="Fetch Data" onPress={fetchData} />
344
</View>
345
);
346
}
347
348
// Check TurboModule availability
349
function checkTurboModuleSupport() {
350
try {
351
// Check if TurboModule system is available
352
const testModule = TurboModuleRegistry.get('TestModule');
353
return true;
354
} catch (error) {
355
console.log('TurboModule system not available');
356
return false;
357
}
358
}
359
360
// Fallback pattern for TurboModules
361
function useTurboModuleWithFallback(turboModuleName, fallbackModuleName) {
362
const [module, setModule] = useState(null);
363
const [isTurbo, setIsTurbo] = useState(false);
364
365
useEffect(() => {
366
try {
367
// Try TurboModule first
368
const turboModule = TurboModuleRegistry.get(turboModuleName);
369
setModule(turboModule);
370
setIsTurbo(true);
371
} catch (error) {
372
// Fallback to regular NativeModule
373
const fallbackModule = NativeModules[fallbackModuleName];
374
if (fallbackModule) {
375
setModule(fallbackModule);
376
setIsTurbo(false);
377
}
378
}
379
}, [turboModuleName, fallbackModuleName]);
380
381
return {module, isTurbo};
382
}
383
```
384
385
```typescript { .api }
386
interface TurboModuleRegistryStatic {
387
get<T extends TurboModule>(name: string): T | null;
388
getEnforcing<T extends TurboModule>(name: string): T;
389
}
390
391
interface TurboModule {
392
getConstants?(): {[key: string]: any};
393
}
394
395
// Example TurboModule interface
396
interface CalculatorTurboModule extends TurboModule {
397
add(a: number, b: number): number;
398
subtract(a: number, b: number): number;
399
multiply(a: number, b: number): number;
400
divide(a: number, b: number): number;
401
power(base: number, exponent: number): Promise<number>;
402
}
403
404
interface NetworkTurboModule extends TurboModule {
405
fetchData(url: string): Promise<any>;
406
uploadFile(path: string, url: string): Promise<{success: boolean; response: any}>;
407
downloadFile(url: string, destination: string): Promise<string>;
408
}
409
```
410
411
## Event Communication
412
413
### NativeEventEmitter
414
415
Create event emitters for communication between JavaScript and native code.
416
417
```javascript { .api }
418
import {NativeEventEmitter, NativeModules} from 'react-native';
419
420
// Create event emitter for native module
421
const {MyNativeModule} = NativeModules;
422
const myModuleEmitter = MyNativeModule ? new NativeEventEmitter(MyNativeModule) : null;
423
424
// Listen to native events
425
function useNativeEvents() {
426
useEffect(() => {
427
if (!myModuleEmitter) return;
428
429
// Add event listeners
430
const subscription1 = myModuleEmitter.addListener('onDataReceived', (data) => {
431
console.log('Data received from native:', data);
432
});
433
434
const subscription2 = myModuleEmitter.addListener('onError', (error) => {
435
console.error('Native error:', error);
436
});
437
438
const subscription3 = myModuleEmitter.addListener('onStatusChanged', (status) => {
439
console.log('Status changed:', status);
440
});
441
442
// Cleanup listeners
443
return () => {
444
subscription1.remove();
445
subscription2.remove();
446
subscription3.remove();
447
};
448
}, []);
449
}
450
451
// Bluetooth event example
452
function BluetoothEventManager() {
453
const {BluetoothModule} = NativeModules;
454
const bluetoothEmitter = BluetoothModule ? new NativeEventEmitter(BluetoothModule) : null;
455
456
const [devices, setDevices] = useState([]);
457
const [connectionStatus, setConnectionStatus] = useState('disconnected');
458
459
useEffect(() => {
460
if (!bluetoothEmitter) return;
461
462
const deviceFoundSubscription = bluetoothEmitter.addListener(
463
'deviceFound',
464
(device) => {
465
setDevices(prevDevices => [...prevDevices, device]);
466
}
467
);
468
469
const connectionSubscription = bluetoothEmitter.addListener(
470
'connectionStatusChanged',
471
(status) => {
472
setConnectionStatus(status);
473
}
474
);
475
476
const dataSubscription = bluetoothEmitter.addListener(
477
'dataReceived',
478
(data) => {
479
console.log('Bluetooth data:', data);
480
}
481
);
482
483
return () => {
484
deviceFoundSubscription.remove();
485
connectionSubscription.remove();
486
dataSubscription.remove();
487
};
488
}, [bluetoothEmitter]);
489
490
const startScan = () => {
491
if (BluetoothModule) {
492
BluetoothModule.startScan();
493
}
494
};
495
496
const connectToDevice = (deviceId) => {
497
if (BluetoothModule) {
498
BluetoothModule.connect(deviceId);
499
}
500
};
501
502
return (
503
<View>
504
<Text>Connection Status: {connectionStatus}</Text>
505
506
<Button title="Start Scan" onPress={startScan} />
507
508
<FlatList
509
data={devices}
510
keyExtractor={item => item.id}
511
renderItem={({item}) => (
512
<TouchableOpacity onPress={() => connectToDevice(item.id)}>
513
<Text>{item.name} - {item.id}</Text>
514
</TouchableOpacity>
515
)}
516
/>
517
</View>
518
);
519
}
520
521
// Location event emitter
522
function LocationTracker() {
523
const {LocationModule} = NativeModules;
524
const locationEmitter = LocationModule ? new NativeEventEmitter(LocationModule) : null;
525
526
const [location, setLocation] = useState(null);
527
const [isTracking, setIsTracking] = useState(false);
528
529
useEffect(() => {
530
if (!locationEmitter) return;
531
532
const locationSubscription = locationEmitter.addListener(
533
'locationUpdate',
534
(newLocation) => {
535
setLocation(newLocation);
536
}
537
);
538
539
const errorSubscription = locationEmitter.addListener(
540
'locationError',
541
(error) => {
542
console.error('Location error:', error);
543
Alert.alert('Location Error', error.message);
544
}
545
);
546
547
return () => {
548
locationSubscription.remove();
549
errorSubscription.remove();
550
};
551
}, [locationEmitter]);
552
553
const startTracking = () => {
554
if (LocationModule) {
555
LocationModule.startLocationUpdates();
556
setIsTracking(true);
557
}
558
};
559
560
const stopTracking = () => {
561
if (LocationModule) {
562
LocationModule.stopLocationUpdates();
563
setIsTracking(false);
564
}
565
};
566
567
return (
568
<View>
569
{location && (
570
<Text>
571
Location: {location.latitude}, {location.longitude}
572
</Text>
573
)}
574
575
<Button
576
title={isTracking ? 'Stop Tracking' : 'Start Tracking'}
577
onPress={isTracking ? stopTracking : startTracking}
578
/>
579
</View>
580
);
581
}
582
583
// Generic event emitter hook
584
function useNativeEventEmitter(nativeModule, events = []) {
585
const [emitter, setEmitter] = useState(null);
586
587
useEffect(() => {
588
if (nativeModule) {
589
const eventEmitter = new NativeEventEmitter(nativeModule);
590
setEmitter(eventEmitter);
591
return () => {
592
// Emitter cleanup is handled by individual subscriptions
593
};
594
}
595
}, [nativeModule]);
596
597
useEffect(() => {
598
if (!emitter || events.length === 0) return;
599
600
const subscriptions = events.map(({eventName, callback}) =>
601
emitter.addListener(eventName, callback)
602
);
603
604
return () => {
605
subscriptions.forEach(subscription => subscription.remove());
606
};
607
}, [emitter, events]);
608
609
return emitter;
610
}
611
612
// Usage with generic hook
613
function GenericNativeComponent() {
614
const {SensorModule} = NativeModules;
615
const [sensorData, setSensorData] = useState({});
616
617
useNativeEventEmitter(SensorModule, [
618
{
619
eventName: 'accelerometerData',
620
callback: (data) => setSensorData(prev => ({...prev, accelerometer: data})),
621
},
622
{
623
eventName: 'gyroscopeData',
624
callback: (data) => setSensorData(prev => ({...prev, gyroscope: data})),
625
},
626
{
627
eventName: 'magnetometerData',
628
callback: (data) => setSensorData(prev => ({...prev, magnetometer: data})),
629
},
630
]);
631
632
return (
633
<View>
634
<Text>Sensor Data:</Text>
635
<Text>{JSON.stringify(sensorData, null, 2)}</Text>
636
</View>
637
);
638
}
639
```
640
641
```typescript { .api }
642
interface NativeEventEmitterStatic {
643
new (nativeModule: any): NativeEventEmitter;
644
}
645
646
interface NativeEventEmitter {
647
addListener(eventType: string, listener: Function): EmitterSubscription;
648
removeAllListeners(eventType?: string): void;
649
listenerCount(eventType: string): number;
650
}
651
652
interface EmitterSubscription {
653
remove(): void;
654
}
655
656
// Device event emitters
657
interface DeviceEventEmitterStatic extends NativeEventEmitter {
658
addListener(eventType: string, listener: Function): EmitterSubscription;
659
}
660
661
interface NativeAppEventEmitterStatic extends NativeEventEmitter {
662
addListener(eventType: string, listener: Function): EmitterSubscription;
663
}
664
```
665
666
## Low-Level Native Integration
667
668
### UIManager
669
670
Access low-level UI manipulation methods for advanced native integration.
671
672
```javascript { .api }
673
import {UIManager, findNodeHandle} from 'react-native';
674
675
// Measure component dimensions
676
function MeasureComponent() {
677
const viewRef = useRef(null);
678
const [dimensions, setDimensions] = useState(null);
679
680
const measureView = () => {
681
if (viewRef.current) {
682
const handle = findNodeHandle(viewRef.current);
683
684
UIManager.measure(handle, (x, y, width, height, pageX, pageY) => {
685
setDimensions({x, y, width, height, pageX, pageY});
686
});
687
}
688
};
689
690
const measureInWindow = () => {
691
if (viewRef.current) {
692
const handle = findNodeHandle(viewRef.current);
693
694
UIManager.measureInWindow(handle, (x, y, width, height) => {
695
console.log('Position in window:', {x, y, width, height});
696
});
697
}
698
};
699
700
return (
701
<View>
702
<View
703
ref={viewRef}
704
style={{width: 200, height: 100, backgroundColor: 'blue'}}
705
>
706
<Text>Measure me</Text>
707
</View>
708
709
<Button title="Measure" onPress={measureView} />
710
<Button title="Measure In Window" onPress={measureInWindow} />
711
712
{dimensions && (
713
<Text>
714
Dimensions: {dimensions.width} x {dimensions.height}
715
Position: ({dimensions.x}, {dimensions.y})
716
Page Position: ({dimensions.pageX}, {dimensions.pageY})
717
</Text>
718
)}
719
</View>
720
);
721
}
722
723
// Focus management
724
function FocusManager() {
725
const inputRef = useRef(null);
726
727
const focusInput = () => {
728
if (inputRef.current) {
729
const handle = findNodeHandle(inputRef.current);
730
UIManager.focus(handle);
731
}
732
};
733
734
const blurInput = () => {
735
if (inputRef.current) {
736
const handle = findNodeHandle(inputRef.current);
737
UIManager.blur(handle);
738
}
739
};
740
741
return (
742
<View>
743
<TextInput
744
ref={inputRef}
745
placeholder="Manageable input"
746
style={styles.input}
747
/>
748
749
<Button title="Focus" onPress={focusInput} />
750
<Button title="Blur" onPress={blurInput} />
751
</View>
752
);
753
}
754
755
// Layout animation configuration
756
function LayoutAnimationManager() {
757
const configureLayoutAnimation = () => {
758
if (UIManager.setLayoutAnimationEnabledExperimental) {
759
UIManager.setLayoutAnimationEnabledExperimental(true);
760
}
761
762
const animationConfig = {
763
duration: 300,
764
create: {
765
type: 'easeInEaseOut',
766
property: 'opacity',
767
},
768
update: {
769
type: 'easeInEaseOut',
770
},
771
delete: {
772
type: 'easeInEaseOut',
773
property: 'opacity',
774
},
775
};
776
777
UIManager.configureNextLayoutAnimation(
778
animationConfig,
779
() => console.log('Animation completed'),
780
(error) => console.error('Animation failed:', error)
781
);
782
};
783
784
return (
785
<Button title="Configure Layout Animation" onPress={configureLayoutAnimation} />
786
);
787
}
788
789
// Custom view manager integration
790
function CustomNativeView() {
791
const viewRef = useRef(null);
792
793
const updateNativeProps = () => {
794
if (viewRef.current) {
795
const handle = findNodeHandle(viewRef.current);
796
797
// Update native properties directly
798
UIManager.updateView(handle, 'MyCustomView', {
799
customProperty: 'newValue',
800
color: '#ff0000',
801
});
802
}
803
};
804
805
const sendCommand = () => {
806
if (viewRef.current) {
807
const handle = findNodeHandle(viewRef.current);
808
809
// Send command to native view
810
UIManager.dispatchViewManagerCommand(
811
handle,
812
'customCommand',
813
['parameter1', 'parameter2']
814
);
815
}
816
};
817
818
return (
819
<View>
820
<View ref={viewRef} style={{width: 200, height: 200}}>
821
<Text>Custom Native View</Text>
822
</View>
823
824
<Button title="Update Native Props" onPress={updateNativeProps} />
825
<Button title="Send Command" onPress={sendCommand} />
826
</View>
827
);
828
}
829
830
// Performance monitoring
831
function PerformanceMonitor() {
832
const [timing, setTiming] = useState({});
833
834
const measureLayoutTime = () => {
835
const startTime = performance.now();
836
837
// Trigger layout calculation
838
UIManager.manuallyApplyRTLCorrection && UIManager.manuallyApplyRTLCorrection(false);
839
840
const endTime = performance.now();
841
setTiming({layout: endTime - startTime});
842
};
843
844
return (
845
<View>
846
<Text>Layout Time: {timing.layout?.toFixed(2)}ms</Text>
847
<Button title="Measure Layout" onPress={measureLayoutTime} />
848
</View>
849
);
850
}
851
```
852
853
```typescript { .api }
854
interface UIManagerStatic {
855
// Measurement
856
measure(
857
node: number,
858
callback: (x: number, y: number, width: number, height: number, pageX: number, pageY: number) => void
859
): void;
860
861
measureInWindow(
862
node: number,
863
callback: (x: number, y: number, width: number, height: number) => void
864
): void;
865
866
measureLayout(
867
node: number,
868
relativeToNativeNode: number,
869
onFail: () => void,
870
onSuccess: (left: number, top: number, width: number, height: number) => void
871
): void;
872
873
// Focus management
874
focus(node: number): void;
875
blur(node: number): void;
876
877
// View updates
878
updateView(node: number, viewName: string, props: any): void;
879
880
// Commands
881
dispatchViewManagerCommand(node: number, commandName: string, commandArgs?: any[]): void;
882
883
// Layout animation
884
configureNextLayoutAnimation(
885
config: any,
886
callback?: () => void,
887
errorCallback?: (error: any) => void
888
): void;
889
890
// Android specific
891
setLayoutAnimationEnabledExperimental?(enabled: boolean): void;
892
893
// Constants
894
getConstants?(): {[key: string]: any};
895
896
// View registry
897
getViewManagerConfig?(viewManagerName: string): any;
898
}
899
900
interface findNodeHandleStatic {
901
(componentOrHandle: any): number | null;
902
}
903
```
904
905
### unstable_batchedUpdates
906
907
Batches multiple state updates into a single render cycle for performance optimization.
908
909
```javascript { .api }
910
import {unstable_batchedUpdates} from 'react-native';
911
912
// Batch multiple state updates
913
function handleBulkUpdate(newItems) {
914
unstable_batchedUpdates(() => {
915
setItems(newItems);
916
setLoading(false);
917
setError(null);
918
setLastUpdated(Date.now());
919
});
920
}
921
922
// In event handlers that might trigger multiple updates
923
function handleComplexUpdate(data) {
924
unstable_batchedUpdates(() => {
925
// All these state updates will be batched together
926
data.forEach(item => {
927
updateItem(item.id, item.data);
928
updateMetadata(item.metadata);
929
updateCache(item.cacheKey, item.value);
930
});
931
});
932
}
933
934
// Useful when working with native events that trigger multiple state changes
935
const handleNativeEvent = (event) => {
936
unstable_batchedUpdates(() => {
937
setPosition(event.position);
938
setVelocity(event.velocity);
939
setTimestamp(event.timestamp);
940
setIsTracking(true);
941
});
942
};
943
```
944
945
```typescript { .api }
946
interface unstable_batchedUpdatesStatic {
947
(callback: () => void): void;
948
}
949
```
950
951
## Native Component Creation
952
953
### requireNativeComponent
954
955
Create React components from native UI components.
956
957
```javascript { .api }
958
import {requireNativeComponent, ViewPropTypes} from 'react-native';
959
960
// Create component from native view
961
const NativeMapView = requireNativeComponent('MapView');
962
963
// Wrapper component with prop validation
964
function MapView({region, markers, onRegionChange, ...props}) {
965
return (
966
<NativeMapView
967
region={region}
968
markers={markers}
969
onRegionChange={onRegionChange}
970
{...props}
971
/>
972
);
973
}
974
975
MapView.propTypes = {
976
region: PropTypes.shape({
977
latitude: PropTypes.number.isRequired,
978
longitude: PropTypes.number.isRequired,
979
latitudeDelta: PropTypes.number.isRequired,
980
longitudeDelta: PropTypes.number.isRequired,
981
}),
982
markers: PropTypes.arrayOf(PropTypes.shape({
983
latitude: PropTypes.number.isRequired,
984
longitude: PropTypes.number.isRequired,
985
title: PropTypes.string,
986
})),
987
onRegionChange: PropTypes.func,
988
...ViewPropTypes,
989
};
990
991
// Custom video player component
992
const NativeVideoPlayer = requireNativeComponent('VideoPlayer');
993
994
function VideoPlayer({source, onProgress, onEnd, style, ...props}) {
995
const handleProgress = (event) => {
996
onProgress?.(event.nativeEvent);
997
};
998
999
const handleEnd = (event) => {
1000
onEnd?.(event.nativeEvent);
1001
};
1002
1003
return (
1004
<NativeVideoPlayer
1005
source={source}
1006
onProgress={handleProgress}
1007
onEnd={handleEnd}
1008
style={[{width: '100%', height: 200}, style]}
1009
{...props}
1010
/>
1011
);
1012
}
1013
1014
// Chart component with native implementation
1015
const NativeChartView = requireNativeComponent('ChartView');
1016
1017
function ChartView({data, chartType, style, onDataPointPress}) {
1018
const handleDataPointPress = (event) => {
1019
const {index, value} = event.nativeEvent;
1020
onDataPointPress?.({index, value});
1021
};
1022
1023
return (
1024
<NativeChartView
1025
data={data}
1026
chartType={chartType}
1027
onDataPointPress={handleDataPointPress}
1028
style={[{height: 300}, style]}
1029
/>
1030
);
1031
}
1032
1033
// Camera view component
1034
const NativeCameraView = requireNativeComponent('CameraView');
1035
1036
function CameraView({onPhotoCapture, onError, ...props}) {
1037
const cameraRef = useRef(null);
1038
1039
const capturePhoto = () => {
1040
if (cameraRef.current) {
1041
const handle = findNodeHandle(cameraRef.current);
1042
UIManager.dispatchViewManagerCommand(handle, 'capturePhoto', []);
1043
}
1044
};
1045
1046
const handlePhotoCapture = (event) => {
1047
const {uri, width, height} = event.nativeEvent;
1048
onPhotoCapture?.({uri, width, height});
1049
};
1050
1051
const handleError = (event) => {
1052
onError?.(event.nativeEvent.error);
1053
};
1054
1055
return (
1056
<View>
1057
<NativeCameraView
1058
ref={cameraRef}
1059
onPhotoCapture={handlePhotoCapture}
1060
onError={handleError}
1061
style={{width: '100%', height: 400}}
1062
{...props}
1063
/>
1064
1065
<Button title="Capture Photo" onPress={capturePhoto} />
1066
</View>
1067
);
1068
}
1069
1070
// Higher-order component for native components
1071
function withNativeComponent(nativeComponentName, defaultProps = {}) {
1072
const NativeComponent = requireNativeComponent(nativeComponentName);
1073
1074
return function WrappedNativeComponent(props) {
1075
const combinedProps = {...defaultProps, ...props};
1076
1077
return <NativeComponent {...combinedProps} />;
1078
};
1079
}
1080
1081
// Usage with HOC
1082
const CustomSlider = withNativeComponent('CustomSlider', {
1083
minimumValue: 0,
1084
maximumValue: 100,
1085
step: 1,
1086
});
1087
```
1088
1089
```typescript { .api }
1090
interface requireNativeComponentStatic {
1091
<T = any>(viewName: string): React.ComponentType<T>;
1092
}
1093
1094
// Native component prop types
1095
interface NativeComponentProps {
1096
style?: ViewStyle;
1097
1098
// Event handlers (converted from native events)
1099
[key: `on${string}`]: ((event: NativeSyntheticEvent<any>) => void) | undefined;
1100
1101
// Other props passed to native component
1102
[key: string]: any;
1103
}
1104
1105
// Example native component interfaces
1106
interface MapViewProps extends NativeComponentProps {
1107
region?: {
1108
latitude: number;
1109
longitude: number;
1110
latitudeDelta: number;
1111
longitudeDelta: number;
1112
};
1113
markers?: Array<{
1114
latitude: number;
1115
longitude: number;
1116
title?: string;
1117
description?: string;
1118
}>;
1119
onRegionChange?: (event: NativeSyntheticEvent<{region: any}>) => void;
1120
onMarkerPress?: (event: NativeSyntheticEvent<{marker: any}>) => void;
1121
}
1122
1123
interface VideoPlayerProps extends NativeComponentProps {
1124
source: {uri: string} | number;
1125
resizeMode?: 'contain' | 'cover' | 'stretch';
1126
repeat?: boolean;
1127
paused?: boolean;
1128
volume?: number;
1129
rate?: number;
1130
onProgress?: (event: NativeSyntheticEvent<{currentTime: number; duration: number}>) => void;
1131
onEnd?: (event: NativeSyntheticEvent<{}>) => void;
1132
onError?: (event: NativeSyntheticEvent<{error: string}>) => void;
1133
}
1134
```
1135
1136
This comprehensive native bridge documentation provides developers with all the tools needed to integrate JavaScript code with native platform functionality, create custom native modules, handle events, and build native UI components in React Native applications.