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.