名称: react-native-components 用户可调用: 否 描述: 当构建 React Native UI 组件时使用,包括核心组件、自定义组件和组件模式。涵盖 View、Text、Image、ScrollView、FlatList 和组件组合。 允许工具:
- 读取
- 写入
- 编辑
- Bash
- Grep
- Glob
React Native 组件
使用此技能在使用 React Native 的核心组件构建用户界面和创建自定义可重用组件时。
核心概念
核心组件
React Native 提供平台无关的组件,映射到原生视图:
import React from 'react';
import {
View,
Text,
Image,
ScrollView,
TextInput,
TouchableOpacity,
SafeAreaView,
} from 'react-native';
export default function App() {
return (
<SafeAreaView style={{ flex: 1 }}>
<ScrollView>
<View>
<Text>你好, React Native!</Text>
<Image
source={{ uri: 'https://example.com/image.jpg' }}
style={{ width: 200, height: 200 }}
/>
<TextInput
placeholder="输入文本"
style={{ borderWidth: 1, padding: 10 }}
/>
<TouchableOpacity onPress={() => console.log('按下')}>
<Text>按下我</Text>
</TouchableOpacity>
</View>
</ScrollView>
</SafeAreaView>
);
}
View 组件
基础构建块:
import { View } from 'react-native';
function Container({ children }: { children: React.ReactNode }) {
return (
<View style={{
flex: 1,
padding: 16,
backgroundColor: '#fff',
}}>
{children}
</View>
);
}
Text 组件
所有文本必须包裹在 <Text> 中:
import { Text } from 'react-native';
function Heading({ children }: { children: string }) {
return (
<Text style={{
fontSize: 24,
fontWeight: 'bold',
color: '#333',
}}>
{children}
</Text>
);
}
function Body({ children }: { children: string }) {
return (
<Text style={{
fontSize: 16,
lineHeight: 24,
color: '#666',
}}>
{children}
</Text>
);
}
Image 组件
显示来自不同源的图像:
import { Image } from 'react-native';
// 远程图像
<Image
source={{ uri: 'https://example.com/image.jpg' }}
style={{ width: 200, height: 200 }}
/>
// 本地图像
<Image
source={require('./assets/logo.png')}
style={{ width: 100, height: 100 }}
/>
// 带有调整大小模式
<Image
source={{ uri: 'https://example.com/image.jpg' }}
style={{ width: 200, height: 200 }}
resizeMode="cover"
/>
最佳实践
使用 SafeAreaView 处理 iOS 刘海
始终使用 SafeAreaView 处理安全区域:
import { SafeAreaView } from 'react-native';
export default function App() {
return (
<SafeAreaView style={{ flex: 1, backgroundColor: '#fff' }}>
{/* 你的内容 */}
</SafeAreaView>
);
}
对长列表使用 FlatList
使用 FlatList 替代 ScrollView 以提高性能:
import { FlatList, Text, View } from 'react-native';
interface Item {
id: string;
title: string;
}
function ItemList({ items }: { items: Item[] }) {
return (
<FlatList
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={{ padding: 16 }}>
<Text>{item.title}</Text>
</View>
)}
// 性能优化
removeClippedSubviews={true}
maxToRenderPerBatch={10}
updateCellsBatchingPeriod={50}
initialNumToRender={10}
windowSize={10}
/>
);
}
可触摸组件
为平台使用适当的可触摸组件:
import { TouchableOpacity, TouchableHighlight, Pressable } from 'react-native';
// 现代方法 - Pressable(推荐)
<Pressable
onPress={() => console.log('按下')}
style={({ pressed }) => [
{ padding: 12, backgroundColor: pressed ? '#ddd' : '#fff' }
]}
>
{({ pressed }) => (
<Text style={{ color: pressed ? '#000' : '#333' }}>按下我</Text>
)}
</Pressable>
// TouchableOpacity - 简单淡入效果
<TouchableOpacity
onPress={() => console.log('按下')}
activeOpacity={0.7}
>
<Text>按下我</Text>
</TouchableOpacity>
组件组合
从简单组件构建复杂 UI:
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
interface CardProps {
title: string;
subtitle?: string;
children?: React.ReactNode;
}
function Card({ title, subtitle, children }: CardProps) {
return (
<View style={styles.card}>
<View style={styles.header}>
<Text style={styles.title}>{title}</Text>
{subtitle && <Text style={styles.subtitle}>{subtitle}</Text>}
</View>
{children && <View style={styles.content}>{children}</View>}
</View>
);
}
const styles = StyleSheet.create({
card: {
backgroundColor: '#fff',
borderRadius: 8,
padding: 16,
marginVertical: 8,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
header: {
marginBottom: 12,
},
title: {
fontSize: 18,
fontWeight: 'bold',
color: '#333',
},
subtitle: {
fontSize: 14,
color: '#666',
marginTop: 4,
},
content: {
marginTop: 8,
},
});
export default Card;
常见模式
带下拉刷新的列表
import React, { useState, useCallback } from 'react';
import { FlatList, RefreshControl, Text, View } from 'react-native';
interface Item {
id: string;
title: string;
}
function RefreshableList({ items, onRefresh }: {
items: Item[];
onRefresh: () => Promise<void>;
}) {
const [refreshing, setRefreshing] = useState(false);
const handleRefresh = useCallback(async () => {
setRefreshing(true);
await onRefresh();
setRefreshing(false);
}, [onRefresh]);
return (
<FlatList
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={{ padding: 16 }}>
<Text>{item.title}</Text>
</View>
)}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />
}
/>
);
}
无限滚动列表
import React from 'react';
import { FlatList, ActivityIndicator, View } from 'react-native';
interface Item {
id: string;
title: string;
}
function InfiniteList({
items,
loading,
onEndReached
}: {
items: Item[];
loading: boolean;
onEndReached: () => void;
}) {
return (
<FlatList
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={{ padding: 16 }}>
<Text>{item.title}</Text>
</View>
)}
onEndReached={onEndReached}
onEndReachedThreshold={0.5}
ListFooterComponent={
loading ? (
<View style={{ padding: 16 }}>
<ActivityIndicator size="large" />
</View>
) : null
}
/>
);
}
Modal 组件
import React from 'react';
import {
Modal,
View,
Text,
TouchableOpacity,
StyleSheet,
} from 'react-native';
interface CustomModalProps {
visible: boolean;
title: string;
children: React.ReactNode;
onClose: () => void;
}
function CustomModal({ visible, title, children, onClose }: CustomModalProps) {
return (
<Modal
visible={visible}
animationType="slide"
transparent={true}
onRequestClose={onClose}
>
<View style={styles.overlay}>
<View style={styles.modal}>
<View style={styles.header}>
<Text style={styles.title}>{title}</Text>
<TouchableOpacity onPress={onClose}>
<Text style={styles.closeButton}>✕</Text>
</TouchableOpacity>
</View>
<View style={styles.content}>{children}</View>
</View>
</View>
</Modal>
);
}
const styles = StyleSheet.create({
overlay: {
flex: 1,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
justifyContent: 'center',
alignItems: 'center',
},
modal: {
width: '80%',
backgroundColor: '#fff',
borderRadius: 12,
padding: 20,
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 16,
},
title: {
fontSize: 20,
fontWeight: 'bold',
},
closeButton: {
fontSize: 24,
color: '#666',
},
content: {
marginTop: 8,
},
});
export default CustomModal;
表单输入组件
import React, { useState } from 'react';
import {
View,
TextInput,
Text,
StyleSheet,
TextInputProps,
} from 'react-native';
interface FormInputProps extends TextInputProps {
label: string;
error?: string;
}
function FormInput({ label, error, ...props }: FormInputProps) {
const [isFocused, setIsFocused] = useState(false);
return (
<View style={styles.container}>
<Text style={styles.label}>{label}</Text>
<TextInput
{...props}
style={[
styles.input,
isFocused && styles.inputFocused,
error && styles.inputError,
]}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
/>
{error && <Text style={styles.error}>{error}</Text>}
</View>
);
}
const styles = StyleSheet.create({
container: {
marginVertical: 8,
},
label: {
fontSize: 14,
fontWeight: '600',
marginBottom: 4,
color: '#333',
},
input: {
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 8,
padding: 12,
fontSize: 16,
},
inputFocused: {
borderColor: '#007AFF',
},
inputError: {
borderColor: '#FF3B30',
},
error: {
color: '#FF3B30',
fontSize: 12,
marginTop: 4,
},
});
export default FormInput;
反模式
不要嵌套 ScrollViews
// 坏 - 嵌套 ScrollViews 导致问题
<ScrollView>
<ScrollView>
<Text>内容</Text>
</ScrollView>
</ScrollView>
// 好 - 使用单个 ScrollView
<ScrollView>
<View>
<Text>内容</Text>
</View>
</ScrollView>
不要对静态值使用内联样式
// 坏 - 每次渲染创建新对象
<View style={{ padding: 16, backgroundColor: '#fff' }}>
<Text>内容</Text>
</View>
// 好 - 使用 StyleSheet
const styles = StyleSheet.create({
container: {
padding: 16,
backgroundColor: '#fff',
},
});
<View style={styles.container}>
<Text>内容</Text>
</View>
不要忘记设置 keyExtractor
// 坏 - 可能导致渲染问题
<FlatList
data={items}
renderItem={({ item }) => <Text>{item.title}</Text>}
/>
// 好 - 提供唯一键
<FlatList
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <Text>{item.title}</Text>}
/>
不要使用索引作为键
// 坏 - 索引作为键会导致重排序问题
<FlatList
data={items}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => <Text>{item.title}</Text>}
/>
// 好 - 使用唯一标识符
<FlatList
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <Text>{item.title}</Text>}
/>
相关技能
- react-native-styling: 使用 StyleSheet 样式化组件
- react-native-navigation: 屏幕间导航
- react-native-performance: 优化组件性能