cri*_*ian 5 back reactjs react-native react-navigation-v5
我在一个项目中使用 React Navigation 5,但在尝试阻止用户在某个时间点后返回时遇到问题。
该应用程序使用类似于以下内容的嵌套导航结构:
ROOT (STACK)
|-- LoginScreens (STACK - options={{ gestureEnabled: false }} )
| |-- Login (SCREEN) -> when successful navigate to "Home"
| +-- Register (SCREEN) -> after registration, navigate to "Login"
|
+-- Home (TABS - options={{ gestureEnabled: false }} )
|-- BlahBlah (SCREEN)
|-- MyProfile (SCREEN)
+-- Dashboard (TABS)
|-- AllTasks (SCREEN)
+-- SomethingElse (SCREEN)
Run Code Online (Sandbox Code Playgroud)
用户成功登录后,用户将被发送到Home屏幕,并且应该无法导航回LoginScreens屏幕。
我尝试在 上使用componentDidMount生命周期方法Home以及useFocusEffect钩子,如下所示:
BackHandler,从处理程序返回 true 工作(true 表示已处理后退动作,不会调用进一步的后退处理程序),但它也会阻止屏幕中的任何后退导航Home(例如,我无法从到 MyProfile 的仪表板)。navigation.reset({ index: 1, routes: [{ name: "Home" }] }). 如果没有index: 1导航,就会回到 ROOT 的 initialRoute(在本例中为LoginScreens)。使用index: 1,Maximum update depth exceeded会引发错误。Home,我曾尝试使用navigation.reset()(注:无参数,可以清除整个浏览历史),并导航到后Home屏幕。这并没有达到预期的效果,因为LoginScreens在导航到 之前,当前路线(ROOT 的 initialRoute,在本例中为:)仍被推送到导航历史记录中Home。Aaaaand...我已经没有想法了。有没有人有什么建议 ?
好吧,我不得不承认,要找到 v5 的新重置方法的语法并不容易,伙计... ReactNavigation 文档确实需要站点内搜索功能。
无论如何,可以使用重置方法,并且非常适合我。
它看起来像:
import { CommonActions } from '@react-navigation/native';
navigation.dispatch(
CommonActions.reset({
index: 0,
routes: [
{
name: 'Home',
params: { user: 'jane' },
},
],
})
);
Run Code Online (Sandbox Code Playgroud)
我做了一个辅助函数,我在我的应用程序的多个地方使用它,看起来像:
import { CommonActions } from '@react-navigation/native';
export const resetStackAndNavigate = (navigation, path) => {
navigation.dispatch(CommonActions.reset({ index: 0, routes: [{ name: path }] }));
};
Run Code Online (Sandbox Code Playgroud)
似乎 React Navigation 的文档试图用本指南涵盖这个用例:
https://reactnavigation.org/docs/en/auth-flow.html
那里的示例非常棘手,已经介绍了状态管理库、reducer、React 钩子以及其他任何没有真正帮助的东西。但是,该指南的摘要是:Conditionally render routes。
取消 React Navigation 4 和之前版本的链接,在 React Navigation 5 中你可以有条件地渲染路由。这样做可以有效地排除导航到不存在路线的任何可能性。下面是一个非常简短的示例,说明如何使用简单的状态变量进行操作。但是请记住,此示例仅考虑了一次渲染一条路线的导航器。如果除了本示例中的路由之外,您还有更多路由,则可能需要调整RootStack.Navigator的道具(initialRouteName例如),或明确导航到特定路由。
import React from "react";
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import LoginNav from "./screens/LoginNav";
import HomeScreens from "./screens/HomeScreens";
const RootStack = createStackNavigator();
export default class MyApp extends React.Component {
constructor(props){
super(props);
this.state = { isLoggedIn: false };
}
setIsLoggedIn = (isLoggedIn)=>{ this.setState({ isLoggedIn }); }
render = () => {
// Using an arrow function to allow to pass setIsLoggedIn to LoginNav
// Pass setIsLoggedIn from the props of LoginNav to the screens it contains
// then from the screens call this function with a true/false param
const LoginScreens = (props)=> <LoginNav {...props} setIsLoggedIn={this.setIsLoggedIn} />
return <NavigationContainer style={{ flex: 1 }}>
<RootStack.Navigator>
{(this.state.isLoggedIn === false)
// If not logged in, the user will be shown this route
? <RootStack.Screen name="LoginScreens" component={LoginScreens} />
// When logged in, the user will be shown this route
: <RootStack.Screen name="Home" component={HomeScreens} />
}
</RootStack.Navigator>
</NavigationContainer>;
}
}
Run Code Online (Sandbox Code Playgroud)
在本例中,调用(this.) props.setIsLoggedIn(true)渲染Home路由,或者调用带false参数返回LoginScreens路由。
希望这个例子比文档中的例子更容易理解。
最初我发布了这个解决方案: /sf/answers/4221492971/
然而,最终,由于我在条件渲染方面遇到了一些严重的卡顿问题,我最终没有使用它:
当Navigator/Screen安装时,会发生很多事情,多个屏幕可能会被实例化(特别是如果您使用没有延迟安装的选项卡式导航器) ),嵌套的<Navigator />s 可能会挂载,react-navigation必须重新评估它的状态,等等。
应用程序别无选择,只能坚持直到整个路线树安装完毕后再进行渲染,这可能会导致安装之间出现空白闪烁。在低端设备上,黑屏持续的时间可能会超出用户所能容忍的时间。
我发现的更好的替代解决方案涉及对NavigationContainer.resetRoot方法的命令式调用。通过将 a 附加ref到NavigationContainer,调用resetRoot将始终作用于根导航状态。
resetRoot还允许指定新的导航状态,这对于更改当前活动的路线很有用。
实现如下:
libs/root-navigation.js
import React from "react";
// This is the ref to attach to the NavigationContainer instance
export const ref = React.createRef();
/**
* Resets the root navigation state, and changes the active route to the one specified
* @param {string} name The name of the route to navigate to after the reset
* @param {object|undefined} params Additional navigation params to pass to the route
*/
export function navigate(name, params) {
try {
ref.current.resetRoot({ index: 0, routes: [{ name, params }] });
} catch (e) {
console.error("Failed to reset the root navigation state. Make sure you have correctly attached the ref to the <NavigationContainer /> component.\nOriginal error:", e);
}
}
Run Code Online (Sandbox Code Playgroud)
App.js(或者无论您在何处渲染<NavigationContainer />组件:
import { NavigationContainer } from "@react-navigation/native";
import * as RootNavigation from "./libs/root-navigation";
import { createStackNavigator } from "@react-navigation/stack";
import LoginScreen from "./screens/Login";
import RegisterScreen from "./screens/Register";
import DashboardScreen from "./screens/Dashboard";
import AccountScreen from "./screens/Account";
const RootStack = createStackNavigator();
const AuthenticationStack = createStackNavigator();
const HomeStack = createStackNavigator();
function AuthenticationScreens() {
return <AuthenticationStack.Navigator initialRouteName="Login">
<AuthenticationStack.Screen name="Login" component={LoginScreen} />
<AuthenticationStack.Screen name="Register" component={RegisterScreen} />
</AuthenticationStack.Navigator>;
}
function HomeScreens() {
return <HomeStack.Navigator initialRouteName="Dashboard">
<HomeStack.Screen name="Dashboard" component={DashboardScreen} />
<HomeStack.Screen name="Account" component={AccountScreen} />
</HomeStack.Navigator>;
}
export default function MyApp() {
// ... your awesome code :)
return <NavigationContainer ref={RootNavigation.ref}>
<RootStack.Navigator initialRouteName="Authentication">
<RootStack.Screen name="Authentication" component={AuthenticationScreens} />
<RootStack.Screen name="Home" component={HomeScreens} />
</RootStack.Navigator>
</NavigationContainer>;
}
Run Code Online (Sandbox Code Playgroud)
然后,在应用程序的其他位置,您始终可以navigate()从root-navigation.js文件导入该函数,并使用它来重置根堆栈:
import { Pressable, Text, View } from "react-native";
import * as RootNavigation from "./libs/root-navigation";
import * as ServerAPI from "./server-api";
function LoginScreen() {
const email = "hello@world.com";
const password = "P@$sw0rD!";
const onLoginPress = () => {
ServerAPI.login(username, password).then(({ success, user })=>{
if (success === true) {
// Here we reset the root navigation state, and navigate to the "Home" screen
RootNavigation.navigate("Home", { user });
} else {
alert("Wrong email or password...");
}
});
}
return <View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Pressable onPress={onLoginPress}>
<Text>Login now!</Text>
</Pressable>
</View>;
}
Run Code Online (Sandbox Code Playgroud)
我更喜欢这个解决方案而不是我最初的解决方案。它也适用于react-navigation@6.x.
| 归档时间: |
|
| 查看次数: |
10483 次 |
| 最近记录: |