How to add subscriptions on defaultNavigationOptions - react-native

I've came across this solution, trying to subscribe to updates to navigation lifecycle
Navigator.js
const App = createStackNavigator(
{
screen1: { screen: MainScreen },
screen2: { screen: SecondaryScreen },
},
{
cardStyle: {
backgroundColor: 'transparent'
}
}
);
const MainNavigator = createAppContainer(App);
export default MainNavigator;
I'm wondering if it's possible to subscribe to events in this navigator file? Or I have no other options but to subscribe at each and every individual screens?

you can add listener for each screen like this
constructor(props) {
super(props);
this.props.navigation.addListener(
'willFocus',//event name
payload => {
//do your work here
}
);
}
other event names:'onDidFocus', 'onWillBlur' ,'onDidBlur'
reference

Related

Disable loading of initialRoute inside stack navigator when

I have two stacks in my react-native app similar to this structure. When I navigate a user from Home screen inside MainStack to Quiz screen inside QuizStack, the componentDidMount method of QuizList screen is being called. How do I prevent this? I want only the componentDidMount of Quiz screen to be called.
export const MainStack = createStackNavigator({
Home: { screen: Home },
Game: { screen: Game },
Leaderboard: { screen: Leaderboard }
}, { initialRouteName: 'Home' })
export const QuizStack = createStackNavigator({
QuizList: { screen: QuizList },
Quiz: { screen: Quiz },
QuizResult: { screen: QuizResult }
}, { initialRouteName: 'QuizList' })
export const InternalStacks = createBottomTabNavigator({
QuizStack: { screen: QuizStack, navigationOptions: { tabBarLabel: 'Quiz' } },
MainStack: { screen: MainStack, navigationOptions: { tabBarLabel: 'Home' } },
})
What makers matters worse is, on sending a push notification, users are directed automatically from Home(MainStack) to Quiz (QuizStack). The componentDidMount method in Quiz calls an API, so if 10K users opens notifications, that 10K APIs called. Then unecesarily the componentDidMount method of QuizList(QuizStack) is also being called, which is an additional 10K APIs
It is unclear to me why you have two separate stacks, and how they are connected. Maybe by a top level navigator? You should try to merge the stacks into a single one.
About the Push notification, you can create a SwitchNavigator:
const SwitchNavigator = createSwitchNavigator(
{
Loading: LoadingScreen,
Root: RootStack
},
{
initialRouteName: 'Loading'
}
)
And in the componentDidMount in LoadingScreen, you will check the initial URL received by the push.
async componentDidMount() {
const { navigation } = this.props
const initialURL = await Linking.getInitialURL()
if (initialURL && initialURL.startsWith('myapp://myapp/xxx')) {
navigation.navigate('MyScreenA', { options })
} else {
navigation.navigate('MyScreenDefaultScreen')
}
}

How to access redux store in routeConfig (react-navigation)

here's the code example:
const mapStateToProps = (state: RootState) => ({
isSwipeEnabled: state.permissions.isSwipeEnabled,
});
const MainNavigator = createMaterialTopTabNavigator(
{
Screen1,
Screen2: {
screen: Screen2,
navigationOptions: {
swipeEnabled: isSwipeEnabled, <==HERE I WANT TO USE MY REDUX STATE
},
},
},
{
initialRouteName: 'Screen1',
tabBarPosition: 'bottom',
},
);
export default connect(mapStateToProps)(MainNavigator);
Is it possible to have access to redux inside createMaterialTopTabNavigator function?
If it is, how?
Thanks!
You can create your navigator in constructor of App component, which is connected to redux. In constructor you can access your mapped state from props:
const mapStateToProps = (state: RootState) => ({
isSwipeEnabled: state.permissions.isSwipeEnabled
});
class NavigationApp extends React.Component {
constructor(props) {
super(props);
this.mainNavigator = createMaterialTopTabNavigator(
{
Screen1,
Screen2: {
screen: Screen2,
navigationOptions: {
// here you now can use your mapped redux state from props
swipeEnabled: props.isSwipeEnabled,
}
}
},
{
initialRouteName: 'Screen1',
tabBarPosition: 'bottom'
},
);
}
render() {
// use here any function from react-navigation which you are using in your environment
// e.g. createAppContainer
// in this example I am using createBrowserApp for using react-navigation in web app
const App = createBrowserApp(this.mainNavigator);
return (<App />);
}
}
export default connect(mapStateToProps)(NavigationApp);

Why is 'this.props.navigation' unavailable to screen component?

I have a drawer navigator that has a stack navigator nested inside it as one of the screen options. I have no issues navigating to any screens in the drawer, but when I try to navigate to another screen in the stack navigator, I dont have access to this.props.navigation. Im confused because the screen is declared in my navigator setup.
AppNavigator:
// all imports
const InboxStack = createStackNavigator(
{
Inbox: {
screen: HomeScreen
},
Conversation: {
screen: ConversationScreen
}
},
{
headerMode: "none",
initialRouteName: "Inbox"
}
);
const MainNavigator = createDrawerNavigator(
{
Inbox: InboxStack,
Second: { screen: SecondScreen },
Third: { screen: ThirdScreen }
},
{
drawerPosition: "left",
initialRouteName: "Inbox",
navigationOptions: ({ navigation }) => ({
title: "Drawer Navigator Header",
headerTitleStyle: {
color: "blue"
},
headerLeft: <Text onPress={() => navigation.toggleDrawer()}>Menu</Text>
})
}
);
const WrapperStackNavigator = createStackNavigator({
drawerNav: MainNavigator
});
const AppContainer = createAppContainer(
createSwitchNavigator(
{
AuthLoading: AuthLoadingScreen,
App: WrapperStackNavigator,
Login: LoginScreen,
Register: RegisterScreen
},
{
initialRouteName: "AuthLoading"
}
)
);
export default AppContainer;
utilization of navigation prop in ConversationScreen:
import React from "react";
import { View, Text } from "react-native";
import { connect } from "react-redux";
import { loadConversation } from "../actions";
import MessagesList from "../components/MessagesList.js";
class ConversationScreen extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.loadConversation();
}
loadConversation() {
this.props.loadConversation(
this.props.navigation.state.params.convoId // this works!
this.props.navigation.getParams("convoId") // this does not :(
);
}
render() {
return (
<View
style={{
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<Text>Conversation screen</Text>
<MessagesList messages={this.props.messages} />
</View>
);
}
}
const mapStateToProps = ({ conversation, auth }) => {
const { messages } = conversation;
const { user } = auth;
return { messages, user };
};
const mapDispatchToProps = {
loadConversation
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(ConversationScreen);
The issue exists with ConversationScreen, I dont have access to the navigation prop, but docs say if its declared in the navigator it should be be passed the navigation prop. Every call using this.props.navigation errors with ... is not a function, ... is undefined
... being this.props.navigation.navigate, this.props.navigation.getParams, etc
After checking the props object through alert(this.props) I verified I had access to the navigation object, even though it appeared to be undefined, ultimately using
componentDidMount() {
loadConversation() {
this.props.loadConversation(
this.props.navigation.state.params.convoId
);
}
}
This got me where I needed to be. although it would have been nice to just use this.props.navigation.getParams()

navigating user on notification click app in background/foreground

I'm using REACT-NATIVE-FCM.
My app has drawer navigator inside stack navigator in App.js.
On notification click from notification tray I want user to directly go to certain Screen.
Registering FCM events in Home page only work if app is closed, while in app in foreground the events are not called.It just takes you to the last open Screen.
I have to register FCM events on every Screen, which doesn’t look ok to me. So I have the solution to register FCM events in App.js since App.js is called from index.js while initializing the App but I’m unable to use this.props.navigation.navigate in App.js.
Using react navigation how can we redirect user just after stack is declared.
APP.JS
const Drawer = DrawerNavigator(
{
Dashboard: { screen: Dashboard },
Screen1: { screen: Screen1 },
Screen2: { screen: Screen2},
Screen3: { screen: Screen3},
},
{
navigationOptions: {
gesturesEnabled: false,
},
initialRouteName: "Dashboard",
contentOptions: {
activeTintColor: "#e91e63"
},
drawerPosition: 'right',
contentComponent: props => <SideBar {...props} />
}
);
const AppNavigator = StackNavigator(
{
Home: { screen: Home },
Drawer: { screen: Drawer },
ScreenABC: { screen: ScreenABC },
ScreenXYZ: { screen: ScreenXYZ }
},
{
headerMode: "none",
}
);
export default () =>
<Root>
<AppNavigator />
</Root>;
//CALLED WHEN APP IN FOREGROUND
this.notificationListener = FCM.on(FCMEvent.Notification, async (notif) => {
//alert('FCM.on: ' + JSON.stringify(notif));
if(notif.routeValue == 1)
{
this.props.navigation.navigate("Screen1")}
});
//CALLED WHEN APP IN BACKGROUND
FCM.getInitialNotification().then(notif => {
});
If you want to really go to a certain screen no matter what. You should at least reset your navigator state before you call navigate.
Or you can also do is creating a custom action, dispatch this action and return a correct navigator state.
Here is how I reset my navigator state to home:
export const homeNav = (state, action) => {
let index;
let routes;
switch (action.type) {
case types.RESET_HOME_NAV:
routes = [...state.routes];
for (let index = state.index; index > 0; --index) {
routes.pop();
}
return {
...state,
routes,
index: 0,
};
default:
return NavigatorHome.router.getStateForAction(action, state);
}};

How to send parameters from screen to stack navigator?

I am a newbie to react native.
I am using a stack navigator inside a tab navigator; I have to navigate multiple screens inside each tab. i am able to send parameters from my default class HomePage to my nested classes in my tab and stack navigators.
export const MyApp = TabNavigator({
Asset: {
screen: AssetScreen,
},
Sensors: {
screen: sensorsStack,
},
Settings: {
screen: settingStack
},
}
export const sensorsStack = StackNavigator({
sensors : { screen: SensorScreen },
sensorDetails : { screen: SensorDetails }
});
export const settingStack = StackNavigator({
settings: { screen: SettingsScreen },
about : { screen: About },
environment : { screen: Environment }
});
export default class HomePage extends Component {
constructor(props) {
super(props);
this.state = {
assetID:'xyz',
authToken:'xyzz'
}
}
static navigationOptions = {
header: null
};
render() {
const screenProps = {
asset: {
assetID: this.state.assetID,
authToken : this.state.authToken,
},
}
return (
<MyApp screenProps={screenProps} />
);
}
}
Now, i want to send a parameter from 'SensorScreen' to 'SensorDetails'. I have tried sending parameters using
NavigationActions.navigate({ routeName: 'sensorDetails' ,params: { sensorType:'Fuel',}});
from 'SensorScreen' class. But was not able to get the parameter in 'SensorDetails' class. How can i pass this params?
A little late answer but might help others that ends up here.
Instead of using NavigationActions you could try navigation from sensorScreen to sensorDetails with navigate and passing some extra variables.
this.props.navigation.navigate('SensorDetails',{sensorType:'Fuel'})
The passed object can then be read in the second screen by
this.props.navigation.state.params.sensorType
A minimal example of the case can be seen in these lines. Observe that this is a stupid on icon tab bar. But it is kept that way since the question was about a tab bar with stacks on each tab. And new tabs are easily added.
import React,{Component} from 'react'
import { Text, View, Footer,Button } from 'react-native'
import {StackNavigator,TabNavigator} from 'react-navigation'
export class SensorScreen extends React.Component {
render() {
return (<Button
onPress={() => this.props.navigation.navigate('SensorDetails',{sensorType:'Fuel'})}
title="Go to Sensor Details"
/>)}}
export class SensorDetails extends React.Component {
render() {
return (<View>
<Text>{this.props.navigation.state.params.sensorType}</Text>
</View>);}
}
const sensorsStack = StackNavigator({
sensors : { screen: SensorScreen },
SensorDetails : { screen: SensorDetails }
});
const MyApp = TabNavigator({
Sensors: {
screen: sensorsStack,
},
});
export default class Nested extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<MyApp/>
);
}
}
Hope this helps.