关于 react-native
面向使用 Expo、导航、样式或原生平台代码的开发者,提供 React Native 应用开发技能。内容包括实现模式、示例及常见移动应用任务的速查信息。
主要功能
- 01带类型的函数组件模式
- 02Expo Router 导航示例
- 03iOS 和 Android 平台代码指导
- 04NativeWind 样式示例
- 05安全区域与键盘处理
适用场景
- 01规划 React Native 应用结构
- 02使用 Expo Router 实现导航
- 03适配 iOS 和 Android 布局
- 04使用 NativeWind 构建界面
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ gentleman-programming/ gentleman-skills --skill react-native安装后在 Claude Code 中输入/react-native确认已加载。
详细介绍
适用场景
在以下情况下加载此技能:
- 使用 React Native 构建移动应用
- 使用 Expo 托管工作流或裸工作流
- 使用 React Navigation 实现导航
- 使用 NativeWind(适用于 RN 的 Tailwind)进行样式设计
- 处理平台特定代码(iOS/Android)
- 管理原生模块和链接
关键模式
模式 1:项目结构
src/
├── app/ # Expo Router screens (if using)
│ ├── (tabs)/ # Tab navigator group
│ ├── (auth)/ # Auth flow group
│ └── _layout.tsx # Root layout
├── components/
│ ├── ui/ # Reusable UI components
│ └── features/ # Feature-specific components
├── hooks/ # Custom hooks
├── services/ # API and external services
├── stores/ # State management (Zustand)
├── utils/ # Utility functions
├── constants/ # App constants, themes
└── types/ # TypeScript types
模式 2:使用 TypeScript 的函数组件
始终使用带有适当类型定义的函数组件:
import { View, Text, Pressable } from 'react-native';
import type { ViewStyle, TextStyle } from 'react-native';
interface ButtonProps {
title: string;
onPress: () => void;
variant?: 'primary' | 'secondary';
disabled?: boolean;
}
export function Button({
title,
onPress,
variant = 'primary',
disabled = false
}: ButtonProps) {
return (
<Pressable
onPress={onPress}
disabled={disabled}
style={({ pressed }) => [
styles.button,
variant === 'secondary' && styles.buttonSecondary,
pressed && styles.buttonPressed,
disabled && styles.buttonDisabled,
]}
>
<Text style={styles.buttonText}>{title}</Text>
</Pressable>
);
}
模式 3:平台特定代码
使用 Platform 模块或文件扩展名处理平台特定代码:
import { Platform, StyleSheet } from 'react-native';
// Using Platform.select
const styles = StyleSheet.create({
container: {
paddingTop: Platform.select({
ios: 44,
android: 0,
}),
...Platform.select({
ios: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
android: {
elevation: 4,
},
}),
},
});
// Or use file extensions:
// Component.ios.tsx
// Component.android.tsx
代码示例
示例 1:Expo Router 导航设置
// app/_layout.tsx
import { Stack } from 'expo-router';
import { StatusBar } from 'expo-status-bar';
export default function RootLayout() {
return (
<>
<StatusBar style="auto" />
<Stack
screenOptions={{
headerShown: false,
animation: 'slide_from_right',
}}
>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen
name="modal"
options={{
presentation: 'modal',
animation: 'slide_from_bottom',
}}
/>
</Stack>
</>
);
}
示例 2:使用 React Query 的自定义 Hook
// hooks/useUser.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { userService } from '@/services/user';
import type { User, UpdateUserInput } from '@/types';
export function useUser(userId: string) {
return useQuery({
queryKey: ['user', userId],
queryFn: () => userService.getById(userId),
staleTime: 5 * 60 * 1000, // 5 minutes
});
}
export function useUpdateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: UpdateUserInput) => userService.update(data),
onSuccess: (_, variables) => {
queryClient.invalidateQueries({ queryKey: ['user', variables.id] });
},
});
}
示例 3:NativeWind 样式设计
// With NativeWind (Tailwind for React Native)
import { View, Text, Pressable } from 'react-native';
import { styled } from 'nativewind';
const StyledPressable = styled(Pressable);
const StyledView = styled(View);
const StyledText = styled(Text);
export function Card({ title, description, onPress }: CardProps) {
return (
<StyledPressable
className="bg-white dark:bg-gray-800 rounded-2xl p-4 shadow-md active:scale-95"
onPress={onPress}
>
<StyledView className="flex-row items-center gap-3">
<StyledView className="w-12 h-12 bg-blue-100 dark:bg-blue-900 rounded-full items-center justify-center">
<StyledText className="text-blue-600 dark:text-blue-300 text-xl">
📱
</StyledText>
</StyledView>
<StyledView className="flex-1">
<StyledText className="text-lg font-semibold text-gray-900 dark:text-white">
{title}
</StyledText>
<StyledText className="text-sm text-gray-500 dark:text-gray-400">
{description}
</StyledText>
</StyledView>
</StyledView>
</StyledPressable>
);
}
示例 4:安全区域和键盘处理
import { KeyboardAvoidingView, Platform } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
export function ScreenWrapper({ children }: { children: React.ReactNode }) {
return (
<SafeAreaView style={{ flex: 1 }} edges={['top', 'left', 'right']}>
<KeyboardAvoidingView
style={{ flex: 1 }}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : 20}
>
{children}
</KeyboardAvoidingView>
</SafeAreaView>
);
}
示例 5:带持久化功能的 Zustand Store
// stores/authStore.ts
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage';
interface AuthState {
token: string | null;
user: User | null;
isAuthenticated: boolean;
login: (token: string, user: User) => void;
logout: () => void;
}
export const useAuthStore = create<AuthState>()(
persist(
(set) => ({
token: null,
user: null,
isAuthenticated: false,
login: (token, user) => set({ token, user, isAuthenticated: true }),
logout: () => set({ token: null, user: null, isAuthenticated: false }),
}),
{
name: 'auth-storage',
storage: createJSONStorage(() => AsyncStorage),
}
)
);
反面模式
不要:到处使用内联样式
// ❌ Bad - inline styles are hard to maintain and don't memoize
export function BadComponent() {
return (
<View style={{ flex: 1, padding: 16, backgroundColor: '#fff' }}>
<Text style={{ fontSize: 18, fontWeight: 'bold', color: '#333' }}>
Title
</Text>
</View>
);
}
// ✅ Good - use StyleSheet or NativeWind
const styles = StyleSheet.create({
container: { flex: 1, padding: 16, backgroundColor: '#fff' },
title: { fontSize: 18, fontWeight: 'bold', color: '#333' },
});
export function GoodComponent() {
return (
<View style={styles.container}>
<Text style={styles.title}>Title</Text>
</View>
);
}
不要:所有场景都使用 TouchableOpacity
// ❌ Bad - TouchableOpacity is legacy
import { TouchableOpacity } from 'react-native';
// ✅ Good - Use Pressable with feedback
import { Pressable } from 'react-native';
<Pressable
onPress={onPress}
style={({ pressed }) => [
styles.button,
pressed && { opacity: 0.7 }
]}
>
{({ pressed }) => (
<Text style={pressed ? styles.textPressed : styles.text}>
Press Me
</Text>
)}
</Pressable>
不要:忘记处理加载和错误状态
// ❌ Bad - no loading/error handling
export function UserProfile({ userId }: { userId: string }) {
const { data } = useUser(userId);
return <Text>{data.name}</Text>; // Will crash if data is undefined
}
// ✅ Good - handle all states
export function UserProfile({ userId }: { userId: string }) {
const { data, isLoading, error } = useUser(userId);
if (isLoading) return <LoadingSpinner />;
if (error) return <ErrorMessage error={error} />;
if (!data) return null;
return <Text>{data.name}</Text>;
}
快速参考
| 任务 | 模式 |
|---|---|
| 创建新的 Expo 项目 | npx create-expo-app@latest --template tabs |
| 添加 NativeWind | npx expo install nativewind tailwindcss |
| 检查平台 | Platform.OS === 'ios' |
| 安全区域内边距 | 从 react-native-safe-area-context 导入 useSafeAreaInsets() |
| 导航 | 使用 Expo Router 执行 router.push('/screen') |
| 深度链接 | 在 app.json 的 expo.scheme 下进行配置 |
| 环境变量 | 使用 expo-constants 或 react-native-dotenv |
| 图标 | @expo/vector-icons(包含在 Expo 中) |
| 动画 | 使用 react-native-reanimated 实现 60fps 动画 |
| 手势 | react-native-gesture-handler |
资源
评论
支持 Markdown