在独立组件 React Native 中获取导航道具
Get navigation props in an independent component React Native
我的 app.js
文件看起来像这样
export default class App extends React.Component {
render() {
return (
<Root style={{
flex: 1
}}>
<FcmHandler/>
</Root>
)
}
}
Root
组件是整个应用程序以及所有功能所在的地方,FcmHandler
是我处理与通知等相关的功能的地方。在 FcmHandler
我有一个单击通知时获取回调的方法,在此回调中,我需要根据通知点击导航到应用程序中的特定屏幕。
问题是使用 FcmHandler
组件上方的当前代码甚至从未初始化。
如果我尝试这样的事情
export default class App extends React.Component {
render() {
return (
<View style={{
flex: 1
}}>
<Root/>
<FcmHandler/>
</View>
)
}
}
FcmHandler
组件被调用,但我无法访问位于 <Root/>
组件内的导航道具。
<Root/>
组件包含以下
const ArticleStack = StackNavigator(
{
...
}
);
const SettingsStack = StackNavigator({
...
});
export const Root = StackNavigator({
Articles: {
screen: ArticleStack
},
Settings: {
screen: SettingsStack
},
}, {
mode: 'modal',
headerMode: 'none'
});
我试图实现的基本目标是,当单击通知时,无论应用当前在哪个屏幕上,我都应该能够导航到特定屏幕。我不想在我拥有的每个屏幕组件中都编写导航代码,这似乎是多余的。
对于 react-navigation
用户,一个非常酷的方法是创建自己的 导航服务
您可以初始化您的 导航服务 模块,在初始化您的导航存储期间,如他们的 docs
中所述
<AppNavigator navigation={addNavigationHelpers({
dispatch: this.props.dispatch,
state: this.props.nav,
addListener,
})} />
// Just add another line to config the navigator object
NavigationService.configNavigator(dispatch) <== This is the important part
NavigationService.js
import { NavigationActions } from 'react-navigation'
let config = {}
const configNavigator = nav => {
config.navigator = nav
}
const reset = (routeName, params) => {
let action = NavigationActions.reset({
index: 0,
key: null,
actions: [
NavigationActions.navigate({
type: 'Navigation/NAVIGATE',
routeName,
params,
}),
],
})
config.navigator(action)
}
const navigate = (routeName, params) => {
let action = NavigationActions.navigate({
type: 'Navigation/NAVIGATE',
routeName,
params,
})
config.navigator(action)
}
const navigateDeep = actions => {
let action = actions.reduceRight(
(prevAction, action) =>
NavigationActions.navigate({
type: 'Navigation/NAVIGATE',
routeName: action.routeName,
params: action.params,
action: prevAction,
}),
undefined
)
config.navigator(action)
}
const goBack = () => {
if (config.navigator) {
let action = NavigationActions.back({})
config.navigator(action)
}
}
export default {
configNavigator,
navigateDeep,
navigate,
reset,
goBack,
}
解释:
每当您的 redux-navigation
初始化时,config
都会初始化 navigator's dispatch
对象,因此您可以 dispatch any navigation action
、wrt 服务组件.
中存在方法
使用
NavigationServices.navigate('ScreenName')
更新:
React Navigation 现在提供 HOC wrapper
withNavigation
,将导航道具传递到包装组件中。
It's useful when you cannot pass the navigation prop into the component directly, or don't want to pass it in case of a deeply nested child.
他们的 docs 中很好地提到了用法。
您可以按照this official guide创建您的导航服务。然后使用 FcmHandler
中的导航服务而不是 navigation
属性。这样就不需要把 FcmHandler
作为导航器的子项了。
如果您使用的是 redux 或 mobx,最好将您的导航状态移动到商店以便于访问。对于 redux,有一个 official integration guide. For mobx, you can try this.
经过一些研究,我发现最简单的方法是遵循他们的 official documentation:
- 我在
./misc
文件夹中创建了一个 RootNavigation.js
文件;
import * as React from 'react';
export const navigationRef = React.createRef();
export function navigate(name, params) {
navigationRef.current?.navigate(name, params);
}
- 我将其导入 App.js 并在 return 函数中创建了对它的引用:
import React from 'react'
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { navigationRef } from './misc/rootNavigation'; <- navigationRef is imported
…
const Stack = createStackNavigator();
function App() {
return (
<Provider store={store}>
<NavigationContainer ref={navigationRef}> <— reference to navigationRef
<Stack.Navigator>
…
<Stack.Screen
name="Screen"
component={Screen}
options={{
title: “Hello”,
headerLeft: () => <ScreenButton/>
}} />
</Stack.Navigator>
</NavigationContainer>
</Provider>
);
}
export default App
- 我在
ScreenButton
组件中调用了它
import React, { Component } from 'react'
…
import * as RootNavigation from '../misc/rootNavigation'; <—- imported
class RoomButton extends Component {
constructor(props) {
super(props)
}
render() {
return (
<TouchableOpacity onPress={
() => {RootNavigation.navigate( 'RoomSelectorScreen' ) <—- called here
…
</TouchableOpacity>
)
}
}
我的 app.js
文件看起来像这样
export default class App extends React.Component {
render() {
return (
<Root style={{
flex: 1
}}>
<FcmHandler/>
</Root>
)
}
}
Root
组件是整个应用程序以及所有功能所在的地方,FcmHandler
是我处理与通知等相关的功能的地方。在 FcmHandler
我有一个单击通知时获取回调的方法,在此回调中,我需要根据通知点击导航到应用程序中的特定屏幕。
问题是使用 FcmHandler
组件上方的当前代码甚至从未初始化。
如果我尝试这样的事情
export default class App extends React.Component {
render() {
return (
<View style={{
flex: 1
}}>
<Root/>
<FcmHandler/>
</View>
)
}
}
FcmHandler
组件被调用,但我无法访问位于 <Root/>
组件内的导航道具。
<Root/>
组件包含以下
const ArticleStack = StackNavigator(
{
...
}
);
const SettingsStack = StackNavigator({
...
});
export const Root = StackNavigator({
Articles: {
screen: ArticleStack
},
Settings: {
screen: SettingsStack
},
}, {
mode: 'modal',
headerMode: 'none'
});
我试图实现的基本目标是,当单击通知时,无论应用当前在哪个屏幕上,我都应该能够导航到特定屏幕。我不想在我拥有的每个屏幕组件中都编写导航代码,这似乎是多余的。
对于 react-navigation
用户,一个非常酷的方法是创建自己的 导航服务
您可以初始化您的 导航服务 模块,在初始化您的导航存储期间,如他们的 docs
中所述 <AppNavigator navigation={addNavigationHelpers({
dispatch: this.props.dispatch,
state: this.props.nav,
addListener,
})} />
// Just add another line to config the navigator object
NavigationService.configNavigator(dispatch) <== This is the important part
NavigationService.js
import { NavigationActions } from 'react-navigation'
let config = {}
const configNavigator = nav => {
config.navigator = nav
}
const reset = (routeName, params) => {
let action = NavigationActions.reset({
index: 0,
key: null,
actions: [
NavigationActions.navigate({
type: 'Navigation/NAVIGATE',
routeName,
params,
}),
],
})
config.navigator(action)
}
const navigate = (routeName, params) => {
let action = NavigationActions.navigate({
type: 'Navigation/NAVIGATE',
routeName,
params,
})
config.navigator(action)
}
const navigateDeep = actions => {
let action = actions.reduceRight(
(prevAction, action) =>
NavigationActions.navigate({
type: 'Navigation/NAVIGATE',
routeName: action.routeName,
params: action.params,
action: prevAction,
}),
undefined
)
config.navigator(action)
}
const goBack = () => {
if (config.navigator) {
let action = NavigationActions.back({})
config.navigator(action)
}
}
export default {
configNavigator,
navigateDeep,
navigate,
reset,
goBack,
}
解释:
每当您的 redux-navigation
初始化时,config
都会初始化 navigator's dispatch
对象,因此您可以 dispatch any navigation action
、wrt 服务组件.
使用
NavigationServices.navigate('ScreenName')
更新:
React Navigation 现在提供 HOC wrapper
withNavigation
,将导航道具传递到包装组件中。
It's useful when you cannot pass the navigation prop into the component directly, or don't want to pass it in case of a deeply nested child.
他们的 docs 中很好地提到了用法。
您可以按照this official guide创建您的导航服务。然后使用 FcmHandler
中的导航服务而不是 navigation
属性。这样就不需要把 FcmHandler
作为导航器的子项了。
如果您使用的是 redux 或 mobx,最好将您的导航状态移动到商店以便于访问。对于 redux,有一个 official integration guide. For mobx, you can try this.
经过一些研究,我发现最简单的方法是遵循他们的 official documentation:
- 我在
./misc
文件夹中创建了一个RootNavigation.js
文件;
import * as React from 'react';
export const navigationRef = React.createRef();
export function navigate(name, params) {
navigationRef.current?.navigate(name, params);
}
- 我将其导入 App.js 并在 return 函数中创建了对它的引用:
import React from 'react'
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { navigationRef } from './misc/rootNavigation'; <- navigationRef is imported
…
const Stack = createStackNavigator();
function App() {
return (
<Provider store={store}>
<NavigationContainer ref={navigationRef}> <— reference to navigationRef
<Stack.Navigator>
…
<Stack.Screen
name="Screen"
component={Screen}
options={{
title: “Hello”,
headerLeft: () => <ScreenButton/>
}} />
</Stack.Navigator>
</NavigationContainer>
</Provider>
);
}
export default App
- 我在
ScreenButton
组件中调用了它
import React, { Component } from 'react'
…
import * as RootNavigation from '../misc/rootNavigation'; <—- imported
class RoomButton extends Component {
constructor(props) {
super(props)
}
render() {
return (
<TouchableOpacity onPress={
() => {RootNavigation.navigate( 'RoomSelectorScreen' ) <—- called here
…
</TouchableOpacity>
)
}
}