or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

animation.mdcore-components.mdindex.mdnative-bridge.mdplatform-apis.mdreact-hooks.mdstyling.mduser-interaction.md

native-bridge.mddocs/

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.