mcpField
R
react-native-patterns

react-native-patterns

github.com/dralgorhythm开发12966 次安装15 周前更新
代码仓库

关于 react-native-patterns

面向 React Native 开发者的组件构建技能,帮助实现易维护的移动界面。内容涵盖交互、列表、导航、无障碍功能及聊天式界面,并针对 React Native 提供实践指导。

主要功能

  1. 01Pressable 触控交互模式
  2. 02FlashList 与 ScrollView 指引
  3. 03底部弹层模式
  4. 04无障碍功能要求
  5. 05聊天式组件模式
  6. 06Expo Router 导航模式

适用场景

  1. 01构建交互式移动界面
  2. 02高效呈现动态列表
  3. 03添加无障碍控件与导航
  4. 04设计带有丰富卡片的聊天界面

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dralgorhythm/claude-agentic-framework --skill react-native-patterns

安装后在 Claude Code 中输入/react-native-patterns确认已加载。

详细介绍

React Native 模式

概述

用于构建易于维护的 React Native 组件的模式。涵盖触摸交互(Pressable)、列表渲染(FlashList)、模态框模式(底部弹出面板)、无障碍功能、导航,以及与 Web React 不同的 RN 特定要求。

有关 React Web 模式(hooks、context、组合),请参阅 react-patterns 技能。

工作流

构建交互式屏幕:

  1. 设置 SafeAreaView 或应用安全区域内边距
  2. 所有交互元素都使用 Pressable(最小尺寸为 44pt)
  3. 在主要操作上添加触觉反馈
  4. 列表使用 FlashList,固定内容使用 ScrollView
  5. 为详情/模态视图实现底部弹出面板
  6. 为所有交互元素添加无障碍标签
  7. 在设备上测试触摸目标和滚动性能

指南

Pressable(而非 TouchableOpacity)

Pressable 是标准的触摸组件。TouchableOpacity 已弃用。

关键原则:

  • 所有交互元素的最小触摸目标尺寸为 44pt
  • 使用 hitSlop 将触摸区域扩展到视觉边界之外:hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
  • 通过 style 函数提供反馈:style={({ pressed }) => [pressed && { opacity: 0.7 }]}
  • 或使用 NativeWind:className="active:opacity-70"
  • 在主要操作的按下事件中添加触觉反馈:onPress={() => { Haptics.impactAsync(); doAction(); }}
纯文本字符串规则

React Native 要求所有可见文本都必须用 <Text> 包裹:

✅ <View><Text>Hello</Text></View>
❌ <View>Hello</View>  // Crashes at runtime

此规则适用于条件渲染、字符串插值和 JSX 表达式。始终使用 Text 包裹字符串。

ScrollView 模式

有两个 className 目标:

  • className — 外层容器(布局、背景)
  • contentContainerClassName — 内层内容(内边距、间距、对齐)

对于内容固定、不动态变化的屏幕,使用 ScrollView。对于动态列表,使用 FlashList。

使用自定义滚动指示器时,设置 showsVerticalScrollIndicator={false} 可让视觉效果更简洁。

FlashList 模式

高性能列表渲染:

  • 必须设置 estimatedItemSize — 估算项目的平均高度(单位为点)
  • renderItem 接收 { item, index } — 保持渲染函数纯净
  • keyExtractor — 使用数据中的唯一字符串 ID
  • contentContainerClassName — 使用 NativeWind 设置内层内容样式
  • ItemSeparatorComponent — 统一项目之间的间距
  • ListEmptyComponent — 优雅地呈现空状态
将底部弹出面板用作模态框

@gorhom/bottom-sheet 可替代 Web 模态对话框:

  • 定义 snapPoints 数组:['25%', '50%', '90%']
  • 使用 BottomSheetModal 和 BottomSheetModalProvider 进行命令式控制
  • 对可滚动的面板内容使用 BottomSheetScrollView
  • 使用 BottomSheetBackdrop 创建按下即可关闭的遮罩层
  • 显示:bottomSheetRef.current?.present()
  • 关闭:bottomSheetRef.current?.dismiss()
expo-image 模式

所有图像显示都使用它:

  • 远程图像:source={{ uri: 'https://...' }}
  • 本地图像:source={require('../assets/image.png')}
  • Blurhash 占位符:加载状态使用 placeholder="LKO2?U%2Tw=w]~RBVZRi}"
  • 卡片缩略图使用 contentFit="cover",完整图像使用 "contain"
  • 使用 transition={300} 实现加载时平滑淡入
无障碍功能

交互元素必须设置以下无障碍属性:

属性用途示例
accessibilityRole语义角色"button"、"link"、"image"
accessibilityLabel屏幕阅读器文本"Open settings"
accessibilityHint操作说明"Opens the settings screen"
accessibilityState动态状态{ selected: true, disabled: false }
accessible标记为可访问Pressable 默认为 true

指南:

  • 所有 Pressable 元素都需要 accessibilityLabel
  • 图像需要使用 accessibilityLabel 描述其内容
  • 使用 accessibilityRole="header" 设置屏幕标题
  • 开关/复选框:设置 accessibilityState={{ checked: isChecked }}
  • 禁用的元素:设置 accessibilityState={{ disabled: true }}
以聊天为核心的组件模式

演示采用以聊天为核心的界面,会话串是主要 UI。

聊天屏幕布局

分为三个区域:

  • 标题栏:保持简洁——标题、返回导航和可选状态。使用顶部安全区域内边距。
  • 消息流:使用 FlashList 或反转的 ScrollView 显示对话。Agent 与用户消息交替出现。富内容嵌入 Agent 消息组件中。
  • 输入栏:固定在底部的文本输入框。使用 KeyboardAvoidingView(iOS)或 android:windowSoftInputMode="adjustResize"。为主屏幕指示条预留底部安全区域内边距。可在文本框上方放置快捷操作标签。
消息气泡

Agent 和用户气泡使用不同的样式(对齐方式、颜色、形状):

  • Agent 气泡:左对齐、中性背景,可包含子组件(卡片、摘要、操作按钮)
  • 用户气泡:右对齐、强调色背景,仅含文本
  • 新消息追加时,气泡使用 FadeInDown.duration(300) 动画显示
内嵌富卡片

作为 Agent 消息气泡的子组件呈现的紧凑数据卡片:

  • 照片缩略图(expo-image)+ 标题 + 关键指标 + 状态徽标
  • 可点击——打开底部弹出层,显示完整详情
  • 对于卡片组:在消息中使用水平 ScrollView,或采用垂直堆叠布局
  • 交错动画:FadeInDown.delay(index * 50).duration(300)
摘要卡片

随着对话进展而更新的累计总数/状态:

  • 显示汇总状态:保留($X)、出售($Y)、捐赠($Z)、进度(N%)
  • 可固定显示在输入栏上方,确保持续可见
  • 更新内容时使用交叉淡入淡出效果(150ms timing)
提示卡片

包含内嵌操作按钮的 Agent 建议:

  • 提示文本 + 1-3 个内联操作按钮
  • 按钮使用 Pressable,触控目标为 44pt,按下时提供触觉反馈
  • 点按后,提示卡片会更新以显示已确认的操作
  • 例如:“要我帮你查找上门取件选项吗?” → [好的] [暂时不用]
对比视图

在消息中展示前后对比或并排内容:

  • 两个带标签(之前/之后)的 expo-image 实例
  • 可选的变化注释(例如,“检测到新的划痕”)
时间线视图

在 Agent 消息中内嵌呈现的按时间顺序排列的事件列表:

  • 紧凑布局:每个事件包含日期 + 标题 + 图标
  • 可展开:点按以显示详情(内联展开或底部弹出层)
  • 事件之间使用垂直连接线
通知消息

作为新消息出现在会话串中的主动 Agent 提醒:

  • 使用醒目样式(图标 + 强调色),以区别于常规 Agent 消息
  • 可包含内联操作按钮
  • 例如:“还有 6 周就进入风暴季。你的地下室仍显示存在潮湿风险。”
组件中的导航

使用 Expo Router 的 useRouter() 钩子:

  • router.push('/path') — 向前导航(添加到堆栈)
  • router.replace('/path') — 替换当前屏幕
  • router.back() — 返回
  • router.canGoBack() — 检查是否可以返回
  • 传递参数:router.push({ pathname: '/detail/[id]', params: { id: '123' } })

最佳实践

  • 始终使用 Pressable,而不是 TouchableOpacity 或 TouchableHighlight
  • 确保最小触控目标为 44pt——视觉尺寸较小时使用 hitSlop
  • 所有可见字符串都必须放在 <Text> 中——JSX 中不能有裸字符串
  • 所有图像都使用 expo-image(绝不使用 RN Image)
  • 为每个交互元素添加 accessibilityLabel
  • 对于超过 10 项的列表,使用带有 estimatedItemSize 的 FlashList
  • 使用 contentContainerClassName 设置 ScrollView/FlashList 内部样式
  • 少量添加触觉反馈——仅用于主要操作
  • 在真实设备上测试触控目标(模拟器触控不够精确)

反模式

  • 使用 TouchableOpacity(已弃用 — 请使用 Pressable)
  • 触控目标小于 44pt,且未使用 hitSlop 补偿
  • 裸字符串未用 <Text> 包裹(会导致运行时崩溃)
  • 使用 RN Image 而非 expo-image(没有缓存,也不支持 blurhash)
  • 忘记为 FlashList 设置 estimatedItemSize
  • 对大型数据集使用 FlatList(FlashList 更快)
  • 交互元素缺少 accessibilityLabel
  • 使用 Alert.alert() 显示复杂选项(请改用底部弹出面板)
  • 在 ScrollView 的 className 中设置内边距(请使用 contentContainerClassName)
  • 在 FlashList 的 renderItem 中内联匿名函数(会导致重复渲染 — 请提取为组件)

评论

支持 Markdown