我正在使用react-native和react-navigation库构建一个移动应用程序,用于管理我的应用程序中的导航。现在,我的应用程序看起来像这样:
App [SwitchNavigator]
Splash [Screen]
Auth [Screen]
MainApp [StackNavigator]
Home [Screen] (/home)
Profile [Screen] (/profile)
Notifications [Screen] (/notifications)
我已经将深度链接与上面的模式集成在一起,用于屏幕Home
,Profile
和Notifications
,它可以按预期工作。我面临的问题是如何在使用深层链接时管理用户的身份验证。现在每当我打开一个深层链接(例如myapp://profile
)时,无论我是否经过身份验证,该应用程序都会将我带到屏幕上。我想要它做的是在AsyncStorage
之前检查是否有userToken
,如果没有或它无效,那么只需重新定向Auth
屏幕。
我以与here描述的方式几乎完全相同的方式设置了身份验证流程。因此,当我的应用程序启动时,Splash
屏幕检查用户的手机是否有有效令牌并将其发送到Auth
屏幕或Home
屏幕。
我现在提出的唯一解决方案是将每个深层链接指向Splash
,对我的用户进行身份验证,然后解析链接以导航到良好的屏幕。因此,例如当用户打开myapp://profile
时,我在Splash
上打开应用程序,验证令牌,然后解析URL(/profile
),最后重定向到Auth
或Profile
。
这是这样做的好方法,还是反应导航提供了更好的方法来做到这一点?他们网站上的Deep linking页面有点亮。
谢谢您的帮助 !
我的设置与您的设置类似。我跟着Authentication flows · React Navigation和SplashScreen - Expo Documentation设置了我的Auth流程,所以我有点失望的是,通过它获得深层链接是一个挑战。我能够通过自定义我的主开关导航器来实现这一点,该方法类似于您所说的解决方案。我只是想分享我的解决方案,所以有一个具体的例子说明如何才能开始工作。我的主切换导航器设置如下(我也使用TypeScript,因此如果不熟悉则忽略类型定义):
const MainNavigation = createSwitchNavigator(
{
SplashLoading,
Onboarding: OnboardingStackNavigator,
App: AppNavigator,
},
{
initialRouteName: 'SplashLoading',
}
);
const previousGetActionForPathAndParams =
MainNavigation.router.getActionForPathAndParams;
Object.assign(MainNavigation.router, {
getActionForPathAndParams(path: string, params: any) {
const isAuthLink = path.startsWith('auth-link');
if (isAuthLink) {
return NavigationActions.navigate({
routeName: 'SplashLoading',
params: { ...params, path },
});
}
return previousGetActionForPathAndParams(path, params);
},
});
export const AppNavigation = createAppContainer(MainNavigation);
您希望通过身份验证流程路由的任何深层链接都需要从auth-link
开始,或者您选择在其前面添加的任何内容。这是SplashLoading
的样子:
export const SplashLoading = (props: NavigationScreenProps) => {
const [isSplashReady, setIsSplashReady] = useState(false);
const _cacheFonts: CacheFontsFn = fonts =>
fonts.map(font => Font.loadAsync(font as any));
const _cacheSplashAssets = () => {
const splashIcon = require(splashIconPath);
return Asset.fromModule(splashIcon).downloadAsync();
};
const _cacheAppAssets = async () => {
SplashScreen.hide();
const fontAssetPromises = _cacheFonts(fontMap);
return Promise.all([...fontAssetPromises]);
};
const _initializeApp = async () => {
// Cache assets
await _cacheAppAssets();
// Check if user is logged in
const sessionId = await SecureStore.getItemAsync(CCSID_KEY);
// Get deep linking params
const params = props.navigation.state.params;
let action: any;
if (params && params.routeName) {
const { routeName, ...routeParams } = params;
action = NavigationActions.navigate({ routeName, params: routeParams });
}
// If not logged in, navigate to Auth flow
if (!sessionId) {
return props.navigation.dispatch(
NavigationActions.navigate({
routeName: 'Onboarding',
action,
})
);
}
// Otherwise, navigate to App flow
return props.navigation.navigate(
NavigationActions.navigate({
routeName: 'App',
action,
})
);
};
if (!isSplashReady) {
return (
<AppLoading
startAsync={_cacheSplashAssets}
onFinish={() => setIsSplashReady(true)}
onError={console.warn}
autoHideSplash={false}
/>
);
}
return (
<View style={{ flex: 1 }}>
<Image source={require(splashIconPath)} onLoad={_initializeApp} />
</View>
);
};
我使用routeName
查询参数创建深层链接,这是执行身份验证后导航到的屏幕的名称(显然可以添加您需要的任何其他查询参数)。由于我的SplashLoading
屏幕处理加载所有字体/资产以及auth检查,我需要每个深层链接来路由它。我遇到的问题是我会从多任务手动退出应用程序,点击深层链接网址,并让应用程序崩溃,因为深层链接绕过SplashLoading
所以没有加载字体。
上面的方法声明了一个action
变量,如果没有设置则不会做任何事情。如果routeName
查询参数不是undefined
,我设置action
变量。这使得一旦Switch路由器决定基于auth(Onboarding
或App
)采用哪条路径,该路由将获得子动作并在退出auth / splash加载流后导航到routeName
。
这是我创建的一个示例链接,可以正常使用此系统:exp://192.168.1.7:19000/--/auth-link?routeName=ForgotPasswordChange&cacheKey=a9b3ra50-5fc2-4er7-b4e7-0d6c0925c536
希望图书馆作者将来能够将其作为本机支持的功能,因此不需要黑客攻击。我也很想看看你提出了什么!
我最终使用自定义URI拦截深层链接启动,然后将这些参数传递到预期路由。我的加载屏幕处理验证检查。
const previousGetActionForPathAndParams = AppContainer.router.getActionForPathAndParams
Object.assign(AppContainer.router, {
getActionForPathAndParams (path, params) {
if (path === 'auth' && params.routeName && params.userId ) {
// returns a profile navigate action for myApp://auth?routeName=chat&userId=1234
return NavigationActions.navigate({
routeName: 'Loading',
params: { ...params, path },
})
}
return previousGetActionForPathAndParams(path, params)
},
})
https://reactnavigation.org/docs/en/routers.html#handling-custom-uris
然后,在我的装载路线中,我像正常一样解析参数,然后路由到预定位置,再次传递它们。
const userId = this.props.navigation.getParam('userId')