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

react-hooks.mddocs/

0

# React Native React Hooks

1

2

React Native provides custom React hooks that integrate with platform APIs and device capabilities, enabling reactive and responsive user interfaces.

3

4

## Installation

5

6

```bash

7

npm install react-native

8

```

9

10

## Dimension and Layout Hooks

11

12

### useWindowDimensions

13

14

React hook for getting current window dimensions with automatic updates on orientation changes and device rotation.

15

16

```javascript { .api }

17

// ESM

18

import {useWindowDimensions} from 'react-native';

19

20

// CommonJS

21

const {useWindowDimensions} = require('react-native');

22

23

// Basic usage

24

function ResponsiveComponent() {

25

const {width, height, scale, fontScale} = useWindowDimensions();

26

27

const isLandscape = width > height;

28

const isTablet = width >= 768;

29

30

return (

31

<View style={{

32

width: width * 0.9,

33

height: isTablet ? height * 0.7 : height * 0.5,

34

backgroundColor: isLandscape ? 'lightblue' : 'lightcoral',

35

}}>

36

<Text>

37

Window: {width} x {height}

38

</Text>

39

<Text>Scale: {scale}</Text>

40

<Text>Font Scale: {fontScale}</Text>

41

<Text>Orientation: {isLandscape ? 'Landscape' : 'Portrait'}</Text>

42

<Text>Device: {isTablet ? 'Tablet' : 'Phone'}</Text>

43

</View>

44

);

45

}

46

47

// Responsive layout hook

48

function useResponsiveLayout() {

49

const {width, height} = useWindowDimensions();

50

51

const breakpoints = {

52

xs: 0,

53

sm: 576,

54

md: 768,

55

lg: 992,

56

xl: 1200,

57

};

58

59

const getBreakpoint = () => {

60

if (width >= breakpoints.xl) return 'xl';

61

if (width >= breakpoints.lg) return 'lg';

62

if (width >= breakpoints.md) return 'md';

63

if (width >= breakpoints.sm) return 'sm';

64

return 'xs';

65

};

66

67

return {

68

width,

69

height,

70

breakpoint: getBreakpoint(),

71

isPhone: width < breakpoints.md,

72

isTablet: width >= breakpoints.md && width < breakpoints.lg,

73

isDesktop: width >= breakpoints.lg,

74

isLandscape: width > height,

75

isPortrait: height >= width,

76

};

77

}

78

79

// Usage with responsive layout

80

function ResponsiveGrid() {

81

const {breakpoint, isTablet} = useResponsiveLayout();

82

83

const getColumns = () => {

84

switch (breakpoint) {

85

case 'xl': return 4;

86

case 'lg': return 3;

87

case 'md': return 2;

88

default: return 1;

89

}

90

};

91

92

const columns = getColumns();

93

const itemWidth = `${100 / columns}%`;

94

95

return (

96

<View style={styles.container}>

97

<Text>Current breakpoint: {breakpoint}</Text>

98

<Text>Columns: {columns}</Text>

99

100

<View style={styles.grid}>

101

{Array.from({length: 12}, (_, i) => (

102

<View

103

key={i}

104

style={[styles.gridItem, {width: itemWidth}]}

105

>

106

<Text>Item {i + 1}</Text>

107

</View>

108

))}

109

</View>

110

</View>

111

);

112

}

113

114

// Dynamic text sizing based on screen size

115

function useResponsiveText() {

116

const {width, fontScale} = useWindowDimensions();

117

118

const getTextSize = (baseSize) => {

119

const screenScale = width / 375; // Base iPhone screen width

120

const scaledSize = baseSize * screenScale;

121

122

// Apply font scale preference

123

return scaledSize * fontScale;

124

};

125

126

return {

127

small: getTextSize(12),

128

body: getTextSize(16),

129

title: getTextSize(20),

130

heading: getTextSize(24),

131

display: getTextSize(32),

132

};

133

}

134

135

// Responsive text component

136

function ResponsiveText({size = 'body', children, style, ...props}) {

137

const textSizes = useResponsiveText();

138

139

return (

140

<Text

141

style={[{fontSize: textSizes[size]}, style]}

142

{...props}

143

>

144

{children}

145

</Text>

146

);

147

}

148

149

// Adaptive component based on dimensions

150

function AdaptiveModal({visible, children, onClose}) {

151

const {width, height, isTablet} = useResponsiveLayout();

152

153

const modalStyle = isTablet ? {

154

width: Math.min(width * 0.8, 600),

155

height: Math.min(height * 0.8, 800),

156

alignSelf: 'center',

157

marginTop: height * 0.1,

158

} : {

159

width: '100%',

160

height: '100%',

161

};

162

163

return (

164

<Modal

165

visible={visible}

166

animationType="slide"

167

presentationStyle={isTablet ? 'formSheet' : 'fullScreen'}

168

onRequestClose={onClose}

169

>

170

<View style={[styles.modalContainer, modalStyle]}>

171

{children}

172

</View>

173

</Modal>

174

);

175

}

176

177

// Keyboard-aware layout with window dimensions

178

function KeyboardAwareLayout({children}) {

179

const {height} = useWindowDimensions();

180

const [keyboardHeight, setKeyboardHeight] = useState(0);

181

182

useEffect(() => {

183

const keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', (e) => {

184

setKeyboardHeight(e.endCoordinates.height);

185

});

186

187

const keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', () => {

188

setKeyboardHeight(0);

189

});

190

191

return () => {

192

keyboardDidShowListener.remove();

193

keyboardDidHideListener.remove();

194

};

195

}, []);

196

197

const availableHeight = height - keyboardHeight;

198

199

return (

200

<View style={{height: availableHeight}}>

201

{children}

202

</View>

203

);

204

}

205

```

206

207

```typescript { .api }

208

interface WindowDimensions {

209

width: number;

210

height: number;

211

scale: number;

212

fontScale: number;

213

}

214

215

interface useWindowDimensionsStatic {

216

(): WindowDimensions;

217

}

218

219

// Custom responsive layout types

220

interface ResponsiveLayout extends WindowDimensions {

221

breakpoint: 'xs' | 'sm' | 'md' | 'lg' | 'xl';

222

isPhone: boolean;

223

isTablet: boolean;

224

isDesktop: boolean;

225

isLandscape: boolean;

226

isPortrait: boolean;

227

}

228

229

interface ResponsiveTextSizes {

230

small: number;

231

body: number;

232

title: number;

233

heading: number;

234

display: number;

235

}

236

```

237

238

## Appearance and Theme Hooks

239

240

### useColorScheme

241

242

React hook for accessing the current color scheme (light/dark mode) with automatic updates when the system appearance changes.

243

244

```javascript { .api }

245

import {useColorScheme} from 'react-native';

246

247

// Basic color scheme detection

248

function ThemedComponent() {

249

const colorScheme = useColorScheme();

250

251

const isDark = colorScheme === 'dark';

252

253

const styles = StyleSheet.create({

254

container: {

255

backgroundColor: isDark ? '#000000' : '#ffffff',

256

flex: 1,

257

},

258

text: {

259

color: isDark ? '#ffffff' : '#000000',

260

},

261

});

262

263

return (

264

<View style={styles.container}>

265

<Text style={styles.text}>

266

Current theme: {colorScheme || 'system default'}

267

</Text>

268

</View>

269

);

270

}

271

272

// Theme provider using color scheme

273

function ThemeProvider({children}) {

274

const colorScheme = useColorScheme();

275

276

const theme = {

277

colors: {

278

primary: colorScheme === 'dark' ? '#bb86fc' : '#6200ee',

279

background: colorScheme === 'dark' ? '#121212' : '#ffffff',

280

surface: colorScheme === 'dark' ? '#1e1e1e' : '#f5f5f5',

281

text: colorScheme === 'dark' ? '#ffffff' : '#000000',

282

textSecondary: colorScheme === 'dark' ? '#b3b3b3' : '#666666',

283

border: colorScheme === 'dark' ? '#333333' : '#e0e0e0',

284

error: colorScheme === 'dark' ? '#cf6679' : '#b00020',

285

success: colorScheme === 'dark' ? '#4caf50' : '#388e3c',

286

warning: colorScheme === 'dark' ? '#ff9800' : '#f57c00',

287

},

288

spacing: {

289

xs: 4,

290

sm: 8,

291

md: 16,

292

lg: 24,

293

xl: 32,

294

},

295

typography: {

296

small: 12,

297

body: 16,

298

subtitle: 18,

299

title: 20,

300

headline: 24,

301

},

302

isDark: colorScheme === 'dark',

303

};

304

305

return (

306

<ThemeContext.Provider value={theme}>

307

{children}

308

</ThemeContext.Provider>

309

);

310

}

311

312

// Custom hook for theme

313

function useTheme() {

314

const context = useContext(ThemeContext);

315

if (!context) {

316

throw new Error('useTheme must be used within ThemeProvider');

317

}

318

return context;

319

}

320

321

// Themed components

322

function ThemedCard({title, children, style}) {

323

const theme = useTheme();

324

325

const cardStyles = StyleSheet.create({

326

card: {

327

backgroundColor: theme.colors.surface,

328

borderRadius: 8,

329

padding: theme.spacing.md,

330

marginVertical: theme.spacing.sm,

331

borderWidth: 1,

332

borderColor: theme.colors.border,

333

...Platform.select({

334

ios: {

335

shadowColor: theme.colors.text,

336

shadowOffset: {width: 0, height: 2},

337

shadowOpacity: theme.isDark ? 0.3 : 0.1,

338

shadowRadius: 4,

339

},

340

android: {

341

elevation: theme.isDark ? 8 : 4,

342

},

343

}),

344

},

345

title: {

346

fontSize: theme.typography.title,

347

fontWeight: 'bold',

348

color: theme.colors.text,

349

marginBottom: theme.spacing.sm,

350

},

351

});

352

353

return (

354

<View style={[cardStyles.card, style]}>

355

{title && <Text style={cardStyles.title}>{title}</Text>}

356

{children}

357

</View>

358

);

359

}

360

361

// Status bar that adapts to color scheme

362

function AdaptiveStatusBar() {

363

const colorScheme = useColorScheme();

364

365

return (

366

<StatusBar

367

barStyle={colorScheme === 'dark' ? 'light-content' : 'dark-content'}

368

backgroundColor={colorScheme === 'dark' ? '#000000' : '#ffffff'}

369

/>

370

);

371

}

372

373

// Navigation theme based on color scheme

374

function useNavigationTheme() {

375

const colorScheme = useColorScheme();

376

377

const lightTheme = {

378

colors: {

379

primary: '#007AFF',

380

background: '#ffffff',

381

card: '#ffffff',

382

text: '#000000',

383

border: '#e0e0e0',

384

notification: '#ff3b30',

385

},

386

};

387

388

const darkTheme = {

389

colors: {

390

primary: '#0A84FF',

391

background: '#000000',

392

card: '#1c1c1e',

393

text: '#ffffff',

394

border: '#333333',

395

notification: '#ff453a',

396

},

397

};

398

399

return colorScheme === 'dark' ? darkTheme : lightTheme;

400

}

401

402

// Image that adapts to color scheme

403

function AdaptiveImage({lightSource, darkSource, ...props}) {

404

const colorScheme = useColorScheme();

405

406

const source = colorScheme === 'dark' ? darkSource : lightSource;

407

408

return <Image source={source} {...props} />;

409

}

410

411

// Color scheme preference hook

412

function useColorSchemePreference() {

413

const systemColorScheme = useColorScheme();

414

const [userPreference, setUserPreference] = useState('system');

415

416

const effectiveColorScheme = userPreference === 'system'

417

? systemColorScheme

418

: userPreference;

419

420

const setColorScheme = (scheme) => {

421

setUserPreference(scheme);

422

// Save to AsyncStorage or preferences

423

};

424

425

return {

426

colorScheme: effectiveColorScheme,

427

userPreference,

428

systemColorScheme,

429

setColorScheme,

430

};

431

}

432

433

// Settings component for color scheme

434

function ColorSchemeSettings() {

435

const {userPreference, setColorScheme} = useColorSchemePreference();

436

437

const options = [

438

{label: 'System', value: 'system'},

439

{label: 'Light', value: 'light'},

440

{label: 'Dark', value: 'dark'},

441

];

442

443

return (

444

<View>

445

<Text style={styles.settingsTitle}>Appearance</Text>

446

{options.map(option => (

447

<TouchableOpacity

448

key={option.value}

449

style={styles.optionRow}

450

onPress={() => setColorScheme(option.value)}

451

>

452

<Text style={styles.optionText}>{option.label}</Text>

453

{userPreference === option.value && (

454

<Text style={styles.checkmark}>✓</Text>

455

)}

456

</TouchableOpacity>

457

))}

458

</View>

459

);

460

}

461

462

// Component that renders differently based on theme

463

function ConditionalContent() {

464

const colorScheme = useColorScheme();

465

466

if (colorScheme === 'dark') {

467

return (

468

<View style={styles.darkContainer}>

469

<Text style={styles.darkText}>Dark mode content</Text>

470

<Image source={require('./assets/dark-logo.png')} />

471

</View>

472

);

473

}

474

475

return (

476

<View style={styles.lightContainer}>

477

<Text style={styles.lightText}>Light mode content</Text>

478

<Image source={require('./assets/light-logo.png')} />

479

</View>

480

);

481

}

482

483

// Hook for creating adaptive styles

484

function useAdaptiveStyles(createStyles) {

485

const colorScheme = useColorScheme();

486

487

return useMemo(() => {

488

return createStyles(colorScheme === 'dark');

489

}, [colorScheme, createStyles]);

490

}

491

492

// Usage with adaptive styles

493

function AdaptiveStyledComponent() {

494

const styles = useAdaptiveStyles((isDark) => StyleSheet.create({

495

container: {

496

backgroundColor: isDark ? '#1a1a1a' : '#f8f9fa',

497

padding: 20,

498

},

499

text: {

500

color: isDark ? '#e0e0e0' : '#333333',

501

fontSize: 16,

502

},

503

button: {

504

backgroundColor: isDark ? '#333333' : '#007AFF',

505

padding: 12,

506

borderRadius: 8,

507

},

508

buttonText: {

509

color: isDark ? '#ffffff' : '#ffffff',

510

textAlign: 'center',

511

},

512

}));

513

514

return (

515

<View style={styles.container}>

516

<Text style={styles.text}>Adaptive styling example</Text>

517

<TouchableOpacity style={styles.button}>

518

<Text style={styles.buttonText}>Button</Text>

519

</TouchableOpacity>

520

</View>

521

);

522

}

523

```

524

525

```typescript { .api }

526

type ColorSchemeName = 'light' | 'dark' | null;

527

528

interface useColorSchemeStatic {

529

(): ColorSchemeName;

530

}

531

532

// Theme-related types

533

interface ThemeColors {

534

primary: string;

535

background: string;

536

surface: string;

537

text: string;

538

textSecondary: string;

539

border: string;

540

error: string;

541

success: string;

542

warning: string;

543

}

544

545

interface ThemeSpacing {

546

xs: number;

547

sm: number;

548

md: number;

549

lg: number;

550

xl: number;

551

}

552

553

interface ThemeTypography {

554

small: number;

555

body: number;

556

subtitle: number;

557

title: number;

558

headline: number;

559

}

560

561

interface Theme {

562

colors: ThemeColors;

563

spacing: ThemeSpacing;

564

typography: ThemeTypography;

565

isDark: boolean;

566

}

567

568

interface ColorSchemePreference {

569

colorScheme: ColorSchemeName;

570

userPreference: 'system' | 'light' | 'dark';

571

systemColorScheme: ColorSchemeName;

572

setColorScheme: (scheme: 'system' | 'light' | 'dark') => void;

573

}

574

```

575

576

## Custom Hook Patterns

577

578

### Creating Reusable Hooks

579

580

```javascript { .api }

581

// Device orientation hook

582

function useDeviceOrientation() {

583

const {width, height} = useWindowDimensions();

584

585

const orientation = width > height ? 'landscape' : 'portrait';

586

587

return {

588

orientation,

589

isLandscape: orientation === 'landscape',

590

isPortrait: orientation === 'portrait',

591

width,

592

height,

593

};

594

}

595

596

// Safe area hook

597

function useSafeArea() {

598

const [safeArea, setSafeArea] = useState({top: 0, bottom: 0, left: 0, right: 0});

599

600

useEffect(() => {

601

// Get safe area insets (would need native implementation)

602

if (Platform.OS === 'ios') {

603

// iOS safe area detection

604

setSafeArea({top: 44, bottom: 34, left: 0, right: 0});

605

} else {

606

// Android safe area detection

607

setSafeArea({top: 24, bottom: 0, left: 0, right: 0});

608

}

609

}, []);

610

611

return safeArea;

612

}

613

614

// Keyboard height hook

615

function useKeyboardHeight() {

616

const [keyboardHeight, setKeyboardHeight] = useState(0);

617

618

useEffect(() => {

619

const keyboardDidShowListener = Keyboard.addListener(

620

'keyboardDidShow',

621

(e) => setKeyboardHeight(e.endCoordinates.height)

622

);

623

624

const keyboardDidHideListener = Keyboard.addListener(

625

'keyboardDidHide',

626

() => setKeyboardHeight(0)

627

);

628

629

return () => {

630

keyboardDidShowListener.remove();

631

keyboardDidHideListener.remove();

632

};

633

}, []);

634

635

return keyboardHeight;

636

}

637

638

// Network status hook

639

function useNetworkStatus() {

640

const [isConnected, setIsConnected] = useState(true);

641

const [connectionType, setConnectionType] = useState('unknown');

642

643

useEffect(() => {

644

// Network status monitoring (would need NetInfo library)

645

const unsubscribe = NetInfo?.addEventListener(state => {

646

setIsConnected(state.isConnected);

647

setConnectionType(state.type);

648

});

649

650

return () => unsubscribe?.();

651

}, []);

652

653

return {

654

isConnected,

655

connectionType,

656

isOffline: !isConnected,

657

};

658

}

659

660

// App state hook

661

function useAppState() {

662

const [appState, setAppState] = useState(AppState.currentState);

663

664

useEffect(() => {

665

const subscription = AppState.addEventListener('change', setAppState);

666

return () => subscription.remove();

667

}, []);

668

669

return {

670

appState,

671

isActive: appState === 'active',

672

isBackground: appState === 'background',

673

isInactive: appState === 'inactive',

674

};

675

}

676

677

// Combined device info hook

678

function useDeviceInfo() {

679

const dimensions = useWindowDimensions();

680

const colorScheme = useColorScheme();

681

const orientation = useDeviceOrientation();

682

const safeArea = useSafeArea();

683

const keyboardHeight = useKeyboardHeight();

684

const appState = useAppState();

685

686

return {

687

...dimensions,

688

colorScheme,

689

...orientation,

690

safeArea,

691

keyboardHeight,

692

...appState,

693

platform: Platform.OS,

694

platformVersion: Platform.Version,

695

};

696

}

697

698

// Usage example

699

function DeviceInfoDisplay() {

700

const deviceInfo = useDeviceInfo();

701

702

return (

703

<ScrollView style={styles.container}>

704

<Text style={styles.title}>Device Information</Text>

705

706

<View style={styles.section}>

707

<Text style={styles.sectionTitle}>Dimensions</Text>

708

<Text>Width: {deviceInfo.width}</Text>

709

<Text>Height: {deviceInfo.height}</Text>

710

<Text>Scale: {deviceInfo.scale}</Text>

711

<Text>Font Scale: {deviceInfo.fontScale}</Text>

712

</View>

713

714

<View style={styles.section}>

715

<Text style={styles.sectionTitle}>Orientation</Text>

716

<Text>Current: {deviceInfo.orientation}</Text>

717

<Text>Is Landscape: {deviceInfo.isLandscape.toString()}</Text>

718

</View>

719

720

<View style={styles.section}>

721

<Text style={styles.sectionTitle}>Appearance</Text>

722

<Text>Color Scheme: {deviceInfo.colorScheme || 'system'}</Text>

723

</View>

724

725

<View style={styles.section}>

726

<Text style={styles.sectionTitle}>Platform</Text>

727

<Text>OS: {deviceInfo.platform}</Text>

728

<Text>Version: {deviceInfo.platformVersion}</Text>

729

</View>

730

731

<View style={styles.section}>

732

<Text style={styles.sectionTitle}>App State</Text>

733

<Text>Current: {deviceInfo.appState}</Text>

734

<Text>Is Active: {deviceInfo.isActive.toString()}</Text>

735

</View>

736

737

<View style={styles.section}>

738

<Text style={styles.sectionTitle}>Keyboard</Text>

739

<Text>Height: {deviceInfo.keyboardHeight}</Text>

740

</View>

741

</ScrollView>

742

);

743

}

744

```

745

746

This comprehensive React hooks documentation provides developers with all the tools needed to create reactive, responsive user interfaces that adapt to device capabilities and user preferences in React Native applications.