mobile-navigation-react-navigation
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseReact Navigation Patterns
React Navigation 模式
Quick Guide: Use the static API for simpler TypeScript inference and automatic deep linking config. Use the dynamic API when you need runtime-dynamic screen lists. Always declare a globalfor type-safeRootParamListeverywhere. UseuseNavigation(not the JS stack) for production performance. Auth flows use conditional screen rendering via thecreateNativeStackNavigatorcallback (static) or conditional JSX (dynamic). Deep linking config lives per-screen in the static API -- no separate config object needed.if
<critical_requirements>
快速指南: 静态API适用于更简洁的TypeScript类型推断和自动深度链接配置。当你需要运行时动态屏幕列表时,使用动态API。务必声明全局,让所有地方的RootParamList都具备类型安全性。生产环境中使用useNavigation(而非JS栈)以保证性能。认证流程通过createNativeStackNavigator回调(静态API)或条件JSX(动态API)实现条件屏幕渲染。静态API中,深度链接配置在每个屏幕中定义——无需单独的配置对象。if
<critical_requirements>
CRITICAL: Before Using This Skill
重要须知:使用此技能前
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,, named constants)import type
(You MUST declare a global interface so is type-safe without manual annotation)
ReactNavigation.RootParamListuseNavigation(You MUST use for production apps -- the JS stack () is significantly slower and only needed for highly custom transitions)
createNativeStackNavigator@react-navigation/stack(You MUST use to navigate back to a previous screen in the stack -- in v7 no longer pops back to existing screens)
popTo()navigate()(You MUST wrap callbacks in -- without it, the effect runs on every render, not just focus changes)
useFocusEffectuseCallback(You MUST NOT use to reach screens in child navigators -- v7 removed implicit nested navigation; use explicit parent targeting)
navigation.navigate('NestedScreen')</critical_requirements>
Auto-detection: React Navigation, @react-navigation, createNativeStackNavigator, createBottomTabNavigator, createDrawerNavigator, createStaticNavigation, NavigationContainer, useNavigation, useRoute, useFocusEffect, usePreventRemove, StaticParamList, StaticScreenProps, NativeStackNavigationProp, CompositeNavigationProp, NavigatorScreenParams, deep linking, linking config, headerSearchBarOptions, headerLargeTitle, popTo, preload
When to use:
- Setting up navigation structure (stack, tab, drawer) in a React Native app
- Choosing between static API and dynamic API for navigator configuration
- Adding type-safe navigation with TypeScript (param lists, typed hooks)
- Configuring deep linking (URL prefixes, path params, universal links)
- Implementing authentication flows with conditional screen rendering
- Customizing headers (large titles, search bars, custom buttons)
- Preloading screens for perceived performance
- Preventing back navigation for unsaved changes
When NOT to use:
- File-based routing with a managed workflow (uses its own router built on React Navigation)
- Web-only React apps (use a web router)
- Simple single-screen apps with no navigation
Key patterns covered:
- Static API vs dynamic API: when to use each
- Global declaration for type-safe hooks everywhere
RootParamList - Native stack vs JS stack performance trade-offs
- Auth flow with conditional screens (static callback or dynamic JSX)
if - Deep linking configuration (per-screen in static, prop in dynamic)
linking - Screen preloading with
navigation.preload() - for screen lifecycle management
useFocusEffect - for unsaved changes guards
usePreventRemove - Header customization: large titles, search bars, form sheets
Detailed Resources:
- examples/core.md - Static API setup, dynamic API setup, type-safe navigation, global RootParamList
- examples/patterns.md - Auth flows, deep linking, modals, tab navigator with nested stacks
- examples/advanced.md - Screen preloading, state persistence, usePreventRemove, useFocusEffect, header customization
- reference.md - Decision frameworks, screen options cheat sheet, v6-to-v7 migration
<philosophy>
所有代码必须遵循CLAUDE.md中的项目约定(短横线命名、命名导出、导入顺序、、命名常量)import type
(你必须声明全局接口,这样无需手动注解就具备类型安全性)
ReactNavigation.RootParamListuseNavigation(生产应用必须使用——JS栈()速度明显更慢,仅在需要高度自定义转场时使用)
createNativeStackNavigator@react-navigation/stack(返回栈中之前的屏幕必须使用——v7中不再返回已有屏幕)
popTo()navigate()(回调必须包裹在中——否则效果会在每次渲染时运行,而非仅在焦点变化时运行)
useFocusEffectuseCallback(禁止使用访问子导航器中的屏幕——v7移除了隐式子导航;需显式指定父级目标)
navigation.navigate('NestedScreen')</critical_requirements>
自动检测: React Navigation, @react-navigation, createNativeStackNavigator, createBottomTabNavigator, createDrawerNavigator, createStaticNavigation, NavigationContainer, useNavigation, useRoute, useFocusEffect, usePreventRemove, StaticParamList, StaticScreenProps, NativeStackNavigationProp, CompositeNavigationProp, NavigatorScreenParams, deep linking, linking config, headerSearchBarOptions, headerLargeTitle, popTo, preload
适用场景:
- 在React Native应用中搭建导航结构(栈、标签、抽屉)
- 为导航器配置选择静态API还是动态API
- 通过TypeScript添加类型安全导航(参数列表、类型化钩子)
- 配置深度链接(URL前缀、路径参数、通用链接)
- 通过条件屏幕渲染实现认证流程
- 自定义头部(大标题、搜索栏、自定义按钮)
- 预加载屏幕以提升感知性能
- 防止未保存更改时的返回导航
不适用场景:
- 使用托管工作流的基于文件的路由(其内置了基于React Navigation的路由)
- 仅Web端的React应用(使用Web路由)
- 无导航需求的简单单屏应用
涵盖的核心模式:
- 静态API vs 动态API:各自适用场景
- 全局声明,让所有钩子都具备类型安全性
RootParamList - Native栈与JS栈的性能权衡
- 带条件屏幕的认证流程(静态回调或动态JSX)
if - 深度链接配置(静态API中按屏幕配置,动态API中使用属性)
linking - 使用预加载屏幕
navigation.preload() - 使用管理屏幕生命周期
useFocusEffect - 使用防止未保存更改
usePreventRemove - 头部自定义:大标题、搜索栏、表单页
详细资源:
- examples/core.md - 静态API设置、动态API设置、类型安全导航、全局RootParamList
- examples/patterns.md - 认证流程、深度链接、模态框、带嵌套栈的标签导航器
- examples/advanced.md - 屏幕预加载、状态持久化、usePreventRemove、useFocusEffect、头部自定义
- reference.md - 决策框架、屏幕选项速查表、v6到v7迁移指南
<philosophy>
Philosophy
设计理念
React Navigation provides routing and navigation for React Native apps. The key decision in v7 is static vs dynamic API:
- Static API -- object-based configuration. Simpler TypeScript (types inferred from config), automatic deep linking path generation, less boilerplate. Use for most apps.
- Dynamic API -- component-based configuration (/
<Stack.Navigator>). Required when screen lists change at runtime or you need full programmatic control over navigator props. More verbose but more flexible.<Stack.Screen>
Both APIs produce the same navigation behavior -- the difference is configuration ergonomics.
Core principles:
- Native stack by default -- uses platform navigation primitives (UINavigationController/Fragment) for smoother transitions and lower memory. The JS stack (
createNativeStackNavigator) only when you need custom transition animations not available natively.@react-navigation/stack - Type safety from the root -- Declare globally so every
ReactNavigation.RootParamListcall is type-checked without manual generics.useNavigation() - Deep linking as first-class -- Configure linking per-screen (static API) or in a centralized config (dynamic API). Prefixes handle custom schemes and universal links.
- Screen lifecycle via focus -- Screens in a stack remain mounted when covered. Use (not
useFocusEffect) for work that should pause when the screen loses focus.useEffect
v7 behavioral changes from v6:
- no longer pops back to existing screens -- use
navigate()insteadpopTo() - Implicit nested navigator navigation removed -- must target parent screen explicitly
- replaced with
headerBackTitleVisibleheaderBackButtonDisplayMode - Navigation state is frozen in dev mode (mutations throw)
- Theme objects now require a property
fonts
<patterns>
React Navigation为React Native应用提供路由和导航功能。v7中的核心决策是静态API vs 动态API:
- 静态API——基于对象的配置。TypeScript使用更简洁(类型从配置中推断)、自动生成深度链接路径、样板代码更少。适用于大多数应用。
- 动态API——基于组件的配置(/
<Stack.Navigator>)。当屏幕列表在运行时变化,或需要对导航器属性进行完全程序化控制时使用。更冗长但更灵活。<Stack.Screen>
两种API产生的导航行为相同——区别在于配置的易用性。
核心原则:
- 默认使用Native栈——使用平台导航原语(UINavigationController/Fragment),实现更流畅的转场和更低的内存占用。仅当需要原生栈不支持的自定义转场动画时,才使用JS栈(
createNativeStackNavigator)。@react-navigation/stack - 从根节点保证类型安全——全局声明,这样每个
ReactNavigation.RootParamList调用都能进行类型检查,无需手动泛型。useNavigation() - 深度链接作为一等公民——在静态API中按屏幕配置链接,或在动态API中使用集中式配置。前缀处理自定义协议和通用链接。
- 通过焦点管理屏幕生命周期——栈中的屏幕被覆盖时仍保持挂载状态。使用(而非
useFocusEffect)处理屏幕失去焦点时应暂停的操作。useEffect
v7相对v6的行为变化:
- 不再返回已有屏幕——改用
navigate()popTo() - 移除了隐式子导航器导航——必须显式指定父级屏幕
- 替换为
headerBackTitleVisibleheaderBackButtonDisplayMode - 开发模式下导航状态被冻结(修改会抛出错误)
- 主题对象现在需要属性
fonts
<patterns>
Core Patterns
核心模式
Pattern 1: Static API Setup
模式1:静态API设置
The static API uses object configuration for simpler TypeScript and automatic deep linking.
typescript
import { createStaticNavigation } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import type { StaticParamList } from "@react-navigation/native";
const RootStack = createNativeStackNavigator({
initialRouteName: "Home",
screenOptions: { headerShown: true },
screens: {
Home: HomeScreen,
Profile: {
screen: ProfileScreen,
linking: "profile/:userId",
},
},
});
const Navigation = createStaticNavigation(RootStack);
// Declare global types -- makes useNavigation() type-safe everywhere
type RootStackParamList = StaticParamList<typeof RootStack>;
declare global {
namespace ReactNavigation {
interface RootParamList extends RootStackParamList {}
}
}
export function App() {
return <Navigation />;
}Why good: types inferred from config (no manual ), deep linking paths defined per-screen, less boilerplate than dynamic API
ParamListSee examples/core.md for complete static API setup with groups and conditional screens.
静态API使用对象配置,实现更简洁的TypeScript支持和自动深度链接。
typescript
import { createStaticNavigation } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import type { StaticParamList } from "@react-navigation/native";
const RootStack = createNativeStackNavigator({
initialRouteName: "Home",
screenOptions: { headerShown: true },
screens: {
Home: HomeScreen,
Profile: {
screen: ProfileScreen,
linking: "profile/:userId",
},
},
});
const Navigation = createStaticNavigation(RootStack);
// 声明全局类型——让所有地方的useNavigation()都具备类型安全性
type RootStackParamList = StaticParamList<typeof RootStack>;
declare global {
namespace ReactNavigation {
interface RootParamList extends RootStackParamList {}
}
}
export function App() {
return <Navigation />;
}优势: 类型从配置中推断(无需手动)、深度链接路径按屏幕定义、比动态API更少的样板代码
ParamList查看examples/core.md了解包含分组和条件屏幕的完整静态API设置。
Pattern 2: Dynamic API Setup
模式2:动态API设置
The dynamic API uses JSX components. Use when screen lists are runtime-dynamic.
typescript
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
type RootStackParamList = {
Home: undefined;
Profile: { userId: string };
};
// Must declare globally for type-safe useNavigation()
declare global {
namespace ReactNavigation {
interface RootParamList extends RootStackParamList {}
}
}
const Stack = createNativeStackNavigator<RootStackParamList>();
export function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}Why good: familiar JSX pattern, supports runtime-dynamic screen lists, manual param list gives explicit control
See examples/core.md for dynamic API with typed hooks and nested navigators.
动态API使用JSX组件。适用于屏幕列表在运行时动态变化的场景。
typescript
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
type RootStackParamList = {
Home: undefined;
Profile: { userId: string };
};
// 必须全局声明以实现类型安全的useNavigation()
declare global {
namespace ReactNavigation {
interface RootParamList extends RootStackParamList {}
}
}
const Stack = createNativeStackNavigator<RootStackParamList>();
export function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}优势: 熟悉的JSX模式、支持运行时动态屏幕列表、手动参数列表提供显式控制
查看examples/core.md了解带类型化钩子和嵌套导航器的动态API设置。
Pattern 3: Type-Safe Navigation Hooks
模式3:类型安全导航钩子
Declare globally once, then and are type-safe everywhere without manual generics.
RootParamListuseNavigation()useRoute()typescript
// In any screen component -- no generic needed
function HomeScreen() {
const navigation = useNavigation();
// Type-checked: "Profile" must exist, params must match
navigation.navigate("Profile", { userId: "123" });
// Type error: "Nonexistent" is not in RootParamList
navigation.navigate("Nonexistent"); // compile error
}For nested navigators, use or to propagate types. With the static API, use for screen component props.
CompositeScreenPropsNavigatorScreenParamsStaticScreenPropsSee examples/core.md for composite types and .
StaticScreenProps全局声明一次,之后和在所有地方都具备类型安全性,无需手动泛型。
RootParamListuseNavigation()useRoute()typescript
// 在任何屏幕组件中——无需泛型
function HomeScreen() {
const navigation = useNavigation();
// 类型检查:"Profile"必须存在,参数必须匹配
navigation.navigate("Profile", { userId: "123" });
// 类型错误:"Nonexistent"不在RootParamList中
navigation.navigate("Nonexistent"); // 编译错误
}对于嵌套导航器,使用或传递类型。在静态API中,使用作为屏幕组件的属性。
CompositeScreenPropsNavigatorScreenParamsStaticScreenProps查看examples/core.md了解复合类型和的使用。
StaticScreenPropsPattern 4: Authentication Flow
模式4:认证流程
Conditionally render auth or main screens. React Navigation animates the transition automatically.
typescript
// Static API: use the `if` callback on groups
const useIsAuthenticated = () => {
const { isAuthenticated } = useContext(AuthContext);
return isAuthenticated;
};
const useIsGuest = () => !useIsAuthenticated();
const RootStack = createNativeStackNavigator({
screens: {},
groups: {
Auth: {
if: useIsGuest,
screenOptions: { headerShown: false },
screens: { Login: LoginScreen, Register: RegisterScreen },
},
Main: {
if: useIsAuthenticated,
screens: { Home: HomeScreen, Profile: ProfileScreen },
},
},
});Why good: callbacks cleanly separate auth/main screens, React Navigation handles transition animation, no manual state-based conditional rendering needed
ifSee examples/patterns.md for both static and dynamic auth flow implementations.
有条件地渲染认证或主屏幕。React Navigation会自动处理转场动画。
typescript
// 静态API:在分组上使用`if`回调
const useIsAuthenticated = () => {
const { isAuthenticated } = useContext(AuthContext);
return isAuthenticated;
};
const useIsGuest = () => !useIsAuthenticated();
const RootStack = createNativeStackNavigator({
screens: {},
groups: {
Auth: {
if: useIsGuest,
screenOptions: { headerShown: false },
screens: { Login: LoginScreen, Register: RegisterScreen },
},
Main: {
if: useIsAuthenticated,
screens: { Home: HomeScreen, Profile: ProfileScreen },
},
},
});优势: 回调清晰分离认证/主屏幕、React Navigation处理转场动画、无需手动基于状态的条件渲染
if查看examples/patterns.md了解静态和动态两种认证流程实现。
Pattern 5: Deep Linking
模式5:深度链接
Static API: define per-screen. Dynamic API: pass a config to .
linkinglinkingNavigationContainertypescript
// Static API -- linking defined inline per screen
const RootStack = createNativeStackNavigator({
screens: {
Home: { screen: HomeScreen, linking: "" },
Profile: {
screen: ProfileScreen,
linking: {
path: "user/:userId",
parse: { userId: (id: string) => id.replace(/^@/, "") },
stringify: { userId: (id: string) => `@${id}` },
},
},
},
});
const Navigation = createStaticNavigation(RootStack);
export function App() {
return (
<Navigation
linking={{ prefixes: ["myapp://", "https://myapp.com"] }}
/>
);
}Why good: linking config co-located with screen definition, parse/stringify handle URL encoding, prefixes handle both custom scheme and universal links
See examples/patterns.md for dynamic API linking, custom URL handlers, and platform-specific setup.
静态API:按屏幕定义。动态API:向传递配置。
linkingNavigationContainerlinkingtypescript
// 静态API——链接配置内联在每个屏幕中
const RootStack = createNativeStackNavigator({
screens: {
Home: { screen: HomeScreen, linking: "" },
Profile: {
screen: ProfileScreen,
linking: {
path: "user/:userId",
parse: { userId: (id: string) => id.replace(/^@/, "") },
stringify: { userId: (id: string) => `@${id}` },
},
},
},
});
const Navigation = createStaticNavigation(RootStack);
export function App() {
return (
<Navigation
linking={{ prefixes: ["myapp://", "https://myapp.com"] }}
/>
);
}优势: 链接配置与屏幕定义共存、parse/stringify处理URL编码、前缀同时处理自定义协议和通用链接
查看examples/patterns.md了解动态API链接、自定义URL处理器和平台特定设置。
Pattern 6: Native Stack vs JS Stack
模式6:Native栈 vs JS栈
Which stack navigator?
|-- Need custom JS-driven transition animations? --> @react-navigation/stack (JS)
|-- Everything else --> @react-navigation/native-stack (NATIVE)| Feature | Native Stack | JS Stack |
|---|---|---|
| Performance | Native animations, lower memory | JS-driven, higher overhead |
| Transitions | Platform defaults + limited custom | Fully customizable |
| Large titles (iOS) | Supported natively | Not available |
| Search bar (iOS) | headerSearchBarOptions | Must build custom |
| Form sheets | presentation: "formSheet" | Not available |
| Gesture handling | Native, smooth | JS-driven |
Default to native stack. Only use JS stack when you need transition animations that native stack cannot provide.
选择哪种栈导航器?
|-- 需要自定义JS驱动的转场动画? --> @react-navigation/stack(JS栈)
|-- 其他场景 --> @react-navigation/native-stack(Native栈)| 特性 | Native栈 | JS栈 |
|---|---|---|
| 性能 | 原生动画,更低内存占用 | JS驱动,更高开销 |
| 转场 | 平台默认 + 有限自定义 | 完全可自定义 |
| iOS大标题 | 原生支持 | 不支持 |
| iOS搜索栏 | headerSearchBarOptions | 必须自定义 |
| 表单页 | presentation: "formSheet" | 不支持 |
| 手势处理 | 原生,流畅 | JS驱动 |
默认使用Native栈。仅当需要Native栈无法提供的转场动画时,才使用JS栈。
Pattern 7: useFocusEffect for Screen Lifecycle
模式7:使用useFocusEffect管理屏幕生命周期
Screens in a stack remain mounted when a new screen is pushed. Use to run effects only when the screen is focused.
useFocusEffecttypescript
import { useCallback } from "react";
import { useFocusEffect } from "@react-navigation/native";
function ChatScreen({ roomId }: { roomId: string }) {
useFocusEffect(
useCallback(() => {
const ws = new WebSocket(`wss://chat.example.com/rooms/${roomId}`);
// Cleanup runs when screen loses focus
return () => ws.close();
}, [roomId]),
);
}Gotcha: The callback MUST be wrapped in . Without it, the effect re-runs on every render, not just focus changes.
useCallbackSee examples/advanced.md for polling, analytics tracking, and resource cleanup patterns.
当新屏幕被推入栈时,栈中的原有屏幕仍保持挂载状态。使用仅在屏幕获得焦点时运行效果。
useFocusEffecttypescript
import { useCallback } from "react";
import { useFocusEffect } from "@react-navigation/native";
function ChatScreen({ roomId }: { roomId: string }) {
useFocusEffect(
useCallback(() => {
const ws = new WebSocket(`wss://chat.example.com/rooms/${roomId}`);
// 屏幕失去焦点时执行清理
return () => ws.close();
}, [roomId]),
);
}注意事项: 回调必须包裹在中。否则效果会在每次渲染时运行,而非仅在焦点变化时运行。
useCallback查看examples/advanced.md了解轮询、分析跟踪和资源清理模式。
Pattern 8: Screen Preloading
模式8:屏幕预加载
Preload heavy screens before the user navigates to them. The screen is rendered off-screen with all hooks running.
typescript
function ProductList() {
const navigation = useNavigation();
const handleLongPress = (productId: string) => {
navigation.preload("ProductDetail", { productId });
};
// Later: navigation.navigate("ProductDetail", { productId }) is instant
}Limitations: Preloaded screens cannot dispatch navigation actions, update options, or listen to events until actually navigated to.
在用户导航到重型屏幕之前预加载它们。屏幕会在离屏状态下渲染,所有钩子都会运行。
typescript
function ProductList() {
const navigation = useNavigation();
const handleLongPress = (productId: string) => {
navigation.preload("ProductDetail", { productId });
};
// 后续:navigation.navigate("ProductDetail", { productId })会瞬间完成
}限制: 预加载的屏幕在实际导航到之前,无法分发导航操作、更新选项或监听事件。
Pattern 9: Header Customization
模式9:头部自定义
Native stack supports platform-native header features: large titles, search bars, and form sheets.
typescript
<Stack.Screen
name="Settings"
component={SettingsScreen}
options={{
headerLargeTitleEnabled: true,
headerLargeStyle: { backgroundColor: "#f5f5f5" },
headerSearchBarOptions: {
placeholder: "Search settings...",
onChangeText: (e) => handleSearch(e.nativeEvent.text),
hideWhenScrolling: true,
},
}}
/>Gotcha: Custom functions disable ALL native header features (large title, search bar, blur effects). Use / to add custom elements while keeping native behavior.
headerheaderLeftheaderRightSee examples/advanced.md for form sheets, custom header items, and search bar integration.
</patterns>
<decision_framework>
Native栈支持平台原生头部特性:大标题、搜索栏和表单页。
typescript
<Stack.Screen
name="Settings"
component={SettingsScreen}
options={{
headerLargeTitleEnabled: true,
headerLargeStyle: { backgroundColor: "#f5f5f5" },
headerSearchBarOptions: {
placeholder: "搜索设置...",
onChangeText: (e) => handleSearch(e.nativeEvent.text),
hideWhenScrolling: true,
},
}}
/>注意事项: 自定义函数会禁用所有原生头部特性(大标题、搜索栏、模糊效果)。使用/添加自定义元素,同时保留原生行为。
headerheaderLeftheaderRight查看examples/advanced.md了解表单页、自定义头部项和搜索栏集成。
</patterns>
<decision_framework>
Decision Framework
决策框架
Static vs Dynamic API
静态API vs 动态API
Starting a new navigation setup?
|-- Can all screens be defined at build time?
| |-- YES --> Static API (simpler TS, auto deep linking)
| +-- NO --> Dynamic API (runtime screen lists)
|
|-- Migrating incrementally from v6?
| +-- YES --> Dynamic API at root, static for new navigators
| (use getComponent() and createPathConfigForStaticNavigation)
|
|-- Need to wrap navigator with providers (e.g. context)?
| +-- Use static API with .with() method开始新的导航设置?
|-- 所有屏幕都能在构建时定义吗?
| |-- 是 --> 静态API(更简洁的TS支持、自动深度链接)
| +-- 否 --> 动态API(运行时屏幕列表)
|
|-- 从v6增量迁移?
| +-- 是 --> 根节点使用动态API,新导航器使用静态API
| (使用getComponent()和createPathConfigForStaticNavigation)
|
|-- 需要用提供者包裹导航器(如context)?
| +-- 使用静态API的.with()方法Navigator Type
导航器类型
What navigation pattern?
|-- Linear flow (onboarding, checkout) --> Stack Navigator
|-- Main app sections with persistent bar --> Bottom Tab Navigator
|-- Side menu / settings panel --> Drawer Navigator
|-- Modal overlays --> Stack with presentation: "modal"
|-- Bottom sheets --> Stack with presentation: "formSheet"
|-- Combination --> Nest navigators (tabs inside stack, stacks inside tabs)选择哪种导航模式?
|-- 线性流程(引导页、结账流程) --> 栈导航器
|-- 带持久化栏的主应用板块 --> 底部标签导航器
|-- 侧边菜单/设置面板 --> 抽屉导航器
|-- 模态覆盖层 --> 栈导航器,设置presentation: "modal"
|-- 底部表单 --> 栈导航器,设置presentation: "formSheet"
|-- 组合模式 --> 嵌套导航器(栈内嵌套标签,标签内嵌套栈)Navigation Method
导航方式
How to move between screens?
|-- Push new screen forward --> navigation.navigate("Screen", params)
|-- Go back to specific screen --> navigation.popTo("Screen", params)
|-- Go back one screen --> navigation.goBack()
|-- Replace current screen --> navigation.replace("Screen", params)
|-- Reset entire stack --> navigation.reset({ routes: [...] })
|-- Navigate to nested screen --> navigation.navigate("Parent", { screen: "Child" })</decision_framework>
<red_flags>
如何在屏幕间跳转?
|-- 向前推入新屏幕 --> navigation.navigate("Screen", params)
|-- 返回指定屏幕 --> navigation.popTo("Screen", params)
|-- 返回上一屏幕 --> navigation.goBack()
|-- 替换当前屏幕 --> navigation.replace("Screen", params)
|-- 重置整个栈 --> navigation.reset({ routes: [...] })
|-- 导航到嵌套屏幕 --> navigation.navigate("Parent", { screen: "Child" })</decision_framework>
<red_flags>
RED FLAGS
注意事项
High Priority Issues:
- Using to go back to a previous screen -- v7 changed behavior;
navigate()stays on current screen if target exists. Usenavigate()instead.popTo() - Using to reach child navigator screens -- removed in v7. Must use
navigation.navigate("NestedScreen").navigate("ParentScreen", { screen: "NestedScreen" }) - Using JS stack () for production without a specific need for custom transitions -- native stack is significantly more performant.
@react-navigation/stack - Missing global declaration -- every
RootParamListcall is untyped, losing the primary benefit of TypeScript with React Navigation.useNavigation() - Using a custom function and expecting native features (large title, search bar, blur) -- custom headers disable all native header functionality.
header
Medium Priority Issues:
- Inline component functions in -- creates a new component on every render, causing unmount/remount. Always pass a reference.
<Stack.Screen component={() => <MyScreen />} /> - Not using for screen-specific side effects --
useFocusEffectruns even when the screen is covered by another screen in the stack.useEffect - Mutating navigation state directly (caught in dev mode in v7, silent corruption in prod).
- Missing property in custom theme -- required in v7, crashes without it.
fonts
Gotchas & Edge Cases:
- callback must be wrapped in
useFocusEffect-- without it, the effect fires on every render, not just focus changesuseCallback - only fires for navigation state removal (back, pop, reset) -- it does NOT fire when the screen is merely unfocused (push, tab switch)
usePreventRemove - Preloaded screens cannot dispatch navigation actions or call until actually navigated to
navigation.setOptions() - Screen can be an object or a function receiving
options-- use the function form when options depend on route params{ route, navigation } - requires
headerSearchBarOptionson your ScrollView/FlatList for proper layoutcontentInsetAdjustmentBehavior="automatic" - replaced
headerBackButtonDisplayModein v7 -- values are "default", "generic", or "minimal"headerBackTitleVisible - removed from tabs/drawer in v7 -- use
unmountOnBluror thepopToTopOnBlur: truepattern insteaduseIsFocused - Navigation state frozen in dev mode -- if you were mutating state directly, you'll get runtime errors in v7 dev builds
- Android requires setup in
RNScreensFragmentFactory-- without it, View state is lost during Activity restartsMainActivity - The component changed from path-based to screen-based:
Linknot<Link screen="Profile" params={{ userId }}><Link to="/profile/123">
</red_flags>
<critical_reminders>
高优先级问题:
- 使用返回之前的屏幕——v7改变了行为;如果目标屏幕已存在,
navigate()会停留在当前屏幕。改用navigate()。popTo() - 使用访问子导航器中的屏幕——v7已移除此功能。必须使用
navigation.navigate("NestedScreen")。navigate("ParentScreen", { screen: "NestedScreen" }) - 在生产环境中使用JS栈()且无自定义转场的特定需求——Native栈性能明显更优。
@react-navigation/stack - 缺少全局声明——每个
RootParamList调用都无类型检查,失去了React Navigation结合TypeScript的主要优势。useNavigation() - 使用自定义函数却期望原生特性(大标题、搜索栏、模糊效果)——自定义头部会禁用所有原生头部功能。
header
中优先级问题:
- 在中使用内联组件函数——每次渲染都会创建新组件,导致卸载/重新挂载。始终传递引用。
<Stack.Screen component={() => <MyScreen />} /> - 未使用处理屏幕特定的副作用——
useFocusEffect即使在屏幕被栈中其他屏幕覆盖时仍会运行。useEffect - 直接修改导航状态(v7开发模式下会被捕获,生产环境中会静默损坏状态)。
- 自定义主题中缺少属性——v7要求必须设置,否则会崩溃。
fonts
陷阱与边缘情况:
- 回调必须包裹在
useFocusEffect中——否则效果会在每次渲染时触发,而非仅在焦点变化时触发useCallback - 仅在导航状态移除(返回、弹出、重置)时触发——屏幕仅失去焦点(推入、切换标签)时不会触发
usePreventRemove - 预加载的屏幕在实际导航到之前,无法分发导航操作或调用
navigation.setOptions() - 屏幕可以是对象或接收
options的函数——当选项依赖路由参数时使用函数形式{ route, navigation } - 要求ScrollView/FlatList设置
headerSearchBarOptions以保证布局正确contentInsetAdjustmentBehavior="automatic" - v7中替代了
headerBackButtonDisplayMode——取值为"default"、"generic"或"minimal"headerBackTitleVisible - v7中移除了标签/抽屉的——改用
unmountOnBlur或popToTopOnBlur: true模式useIsFocused - 开发模式下导航状态被冻结——如果之前直接修改状态,v7开发构建中会出现运行时错误
- Android需要在中设置
MainActivity——否则Activity重启时会丢失View状态RNScreensFragmentFactory - 组件从基于路径改为基于屏幕:使用
Link而非<Link screen="Profile" params={{ userId }}><Link to="/profile/123">
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
重要提醒
All code must follow project conventions in CLAUDE.md
(You MUST declare a global interface so is type-safe without manual annotation)
ReactNavigation.RootParamListuseNavigation(You MUST use for production apps -- the JS stack () is significantly slower and only needed for highly custom transitions)
createNativeStackNavigator@react-navigation/stack(You MUST use to navigate back to a previous screen in the stack -- in v7 no longer pops back to existing screens)
popTo()navigate()(You MUST wrap callbacks in -- without it, the effect runs on every render, not just focus changes)
useFocusEffectuseCallback(You MUST NOT use to reach screens in child navigators -- v7 removed implicit nested navigation; use explicit parent targeting)
navigation.navigate('NestedScreen')Failure to follow these rules will cause untyped navigation, performance issues, broken back navigation, and runtime errors.
</critical_reminders>
所有代码必须遵循CLAUDE.md中的项目约定
(你必须声明全局接口,这样无需手动注解就具备类型安全性)
ReactNavigation.RootParamListuseNavigation(生产应用必须使用——JS栈()速度明显更慢,仅在需要高度自定义转场时使用)
createNativeStackNavigator@react-navigation/stack(返回栈中之前的屏幕必须使用——v7中不再返回已有屏幕)
popTo()navigate()(回调必须包裹在中——否则效果会在每次渲染时运行,而非仅在焦点变化时运行)
useFocusEffectuseCallback(禁止使用访问子导航器中的屏幕——v7移除了隐式子导航;需显式指定父级目标)
navigation.navigate('NestedScreen')不遵循这些规则会导致无类型导航、性能问题、返回导航失效和运行时错误。
</critical_reminders>