我正在构建一个带有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页面有点亮。
感谢您的帮助!
答案 0 :(得分:2)
我的设置类似于您的设置。我遵循了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
查询参数创建了深层链接,查询参数是执行了auth检查之后可以导航到的屏幕的名称(显然,您可以添加所需的任何其他查询参数)。由于我的SplashLoading
屏幕可处理所有字体/资产的加载以及身份验证,因此我需要每个深层链接来进行路由。我遇到的问题是,我将手动从多任务处理中退出应用程序,点击一个深层链接URL,并由于深层链接绕过SplashLoading
而导致应用程序崩溃,因此字体没有被加载。
以上方法声明了一个action
变量,如果未设置该变量将不起作用。如果routeName
查询参数不是undefined
,则设置action
变量。这样一来,一旦Switch路由器根据auth(Onboarding
或App
决定采用哪个路径,该路由就会获得子操作并在退出auth /之后导航到routeName
。飞溅加载流。
以下是我创建的一个示例链接,该链接在此系统上正常运行:
exp://192.168.1.7:19000/--/auth-link?routeName=ForgotPasswordChange&cacheKey=a9b3ra50-5fc2-4er7-b4e7-0d6c0925c536
希望图书馆作者将来会将此功能作为本机支持的功能,因此无需进行黑客攻击。我也很想知道您的想法!
答案 1 :(得分:1)
我最终使用了一个自定义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')
答案 2 :(得分:1)
在我这方面,我无需手动解析路径以提取路径和参数即可实现此目的。 步骤如下:
const previousGetActionForPathAndParams = AppContainer.router.getActionForPathAndParams
Object.assign(AppContainer.router, {
getActionForPathAndParams(path: string, params: NavigationParams) {
const navigationAction = previousGetActionForPathAndParams(path, params)
return NavigationActions.navigate({
routeName: 'Authentication',
params: { navigationAction }
})
}
})
然后在“身份验证”视图中:
const navigationAction = this.navigation.getParam('navigationAction')
if (navigationAction)
this.props.navigation.dispatch(navigationAction)