I am trying to track screen names on react-native-firebase in conjunction with react-navigation.
Here is my code.
const tracker = firebase.analytics()
function getCurrentRouteName(navigationState) {
if (!navigationState) {
return null;
}
const route = navigationState.routes[navigationState.index];
// dive into nested navigators
if (route.routes) {
return getCurrentRouteName(route);
}
return route.routeName;
}
export default class AppNavigation extends Component {
render() {
StatusBar.setBarStyle('light-content');
return (
<MainScreenNavigator
onNavigationStateChange={(prevState, currentState) => {
const currentScreen = getCurrentRouteName(currentState);
const prevScreen = getCurrentRouteName(prevState);
if (prevScreen !== currentScreen) {
// the line below uses the Google Analytics tracker
// change the tracker here to use other Mobile analytics SDK.
tracker.setCurrentScreen(currentScreen);
}
}}
/>
);
}
}
When I console log the screen names, they appear as desired. However, I'm not seeing the results in Firebase console. When I filter screen by name it just says (not set). Am I doing something wrong in my code? I am importing firebase from 'react-native-firebase' as well.
The code above is solid. It turns out you have to wait a half a day or so before data is populated. Not sure if I missed that in the docs. If you're using react-navigation and firebase, this code works!
Related
I have a problem how to pass user data after logging in via the 'profile' screen BUT it is not switched to the profile screen?
I only know the code below is the data transfer is complete then the screen will switch? I searched a lot on the internet but there was none or maybe I don't know how to search for this keyword.
this.props.navigation.navigate('Profile', {
data: this.state.data
});
You can use Async Storage to access data after switching navigators.
Also, I'm not sure what's the problem with passing data with navigation.navigate. You can use getParam() to get data on the next screen.
Update
Send data:
this.props.navigation.navigate('Profile', {
nickName: 'MohamadKh75'
});
Get data in Profile screen:
const name = this.props.navigation.getParam('nickName', 'defaultValue'); // name will be 'MohamadKh75'
const age = this.props.navigation.getParam('age', 123); // as we didn't pass 'age', the value is 123
Check here for more information!
If you don't want to switch screens but want to pass data across screens, first, since you don't want to switch screens, you don't need this:
this.props.navigation.navigate('Profile', {
data: this.state.data
});
And instead, you need this:
function_name() {
// some code we will discuss below...
}
Second, since you want the data available across screens or more specifically send the data across screens but not go to that screens, you can use local storage like Async Storage (or SQL lite for big projects). That way, the data is available between screens, but not go to that screens.
For example, in Async Storage, you can store data in local but not switch screens:
async storeData(key, value) {
try {
await AsyncStorage.setItem(key, value);
}
}
async button(key, value) {
await this.storeData(key, value);
// In case you like to switch screens anywhere you want,
// you can uncomment the code below:
// this.props.navigation.navigate('you screens')
}
render() {
return(
<Button onPress={() => this.button("data", this.state.data) } />
);
}
Then, you can retrieve it anywhere you want on your screens, whether you want to switch screens or not:
async getData(key) {
try {
const value = await AsyncStorage.getItem(key);
if (value !== null) {
return value;
}
}
}
async button(key) {
const data = await this.getData(key);
}
render() {
return(
<Button onPress={() => this.button("data") } />
);
}
I want to navigate the user to another screen in react native project after native app widget click in android. I was able to catch event using native event emitter in my MainView.js and there i changed state of one of my component and it got changed but UI is not getting rendered after this state change. It is showing blank screen and there is not error on the console. Thanks in advance for any help!!
export default class MainView extends React.Component {
constructor(props) {
super(props);
this.state = {text: 'Hi, This is main screen for app widget!!!'};
}
componentDidMount() {
const eventEmitter = new NativeEventEmitter();
this.listener = eventEmitter.addListener('MyCustomEvent', (event) => {
console.log('MyCustomEvent -->', event);
console.log('MyCustomEvent ArticleId -->', event.ArticleId);
if (event.ArticleId === data.articleId) {
console.log('data ArticleId true', data.articleId);
//navigation.push('Article Details', data);
this.setState({
text: data.articleDes,
});
// setText(data.articleDes);
console.log('text -->', this.state.text);
} else {
// setText('No such article found.');
console.log('text -->', this.state.text);
}
});
}
componentWillUnmount() {
this.eventListener.remove(); //Removes the listener
}
render() {
return (
<View style={{flex: 1}}>
<Text>{this.state.text}</Text>
<Button
title="click"
onPress={() => this.props.navigation.push('Article Details', data)}
/>
</View>
);
}
}
CustomActivity source code which is launched from appwidget click. From this activity's oncreate, I'm emitting events to react-native main view.
int articleId = 0;
if (getIntent() != null) {
articleId = getIntent().getIntExtra("articleId", 0);
Log.e("articleid", "" + articleId);
}
// Put data to map
WritableMap payload = Arguments.createMap();
payload.putInt("ArticleId", articleId);
// Emitting event from java code
ReactContext context = getReactNativeHost().getReactInstanceManager().getCurrentReactContext();
if ( context != null && context.hasActiveCatalystInstance()) {
Log.e("react context", "not null");
(getReactNativeHost().getReactInstanceManager().getCurrentReactContext())
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("MyCustomEvent", payload);
}
That is not how to use NativeEventEmitter. You need to initialise the NativeEventEmitter with the native module you are emitting events from:
import { NativeEventEmitter, NativeModules } from 'react-native';
const { myNativeModule } = NativeModules;
componentDidMount() {
...
const eventEmitter = new NativeEventEmitter(myNativeModule);
this.eventListener = eventEmitter.addListener('myEvent', (event) => {
console.log(event.eventProperty) // "someValue"
});
...
}
componentWillUnmount() {
this.eventListener.remove(); //Removes the listener
}
Read more about NativeModules here: https://reactnative.dev/docs/native-modules-android
This sound familiar with an issue I am experiencing on IOS. The code is similar, but I cannot guarantee that the underlying structure in Android works in the same way. Anyways, I am sending an event message from IOS-Native (written in swift in xCode) to React-native file using the NativeEventEmitter. After the initial render, the value just wont update, and as I understand this issue is not limited to this type of Event. After some googling I found out that everything you read from state inside that event-callback has a reference to only the first render, and will not update on future renders.
Solution; use useRef so you keep a reference to the the updated value. useRef keeps the value across renders and event-callbacks. This is not something I have found out myself, please look at https://medium.com/geographit/accessing-react-state-in-event-listeners-with-usestate-and-useref-hooks-8cceee73c559 and React useState hook event handler using initial state for, they are the one that deserves the credit.
I'm building a react native app using expo and I would like to know how I can send a "UserTypeA" to Homepage and send a "UserTypeB" to Profile upon login.
I have a UserTypeA tab navigator and a UserTypeB tab navigator, with just 2 pages that will be see able by both accounts.
I have my UserTypeA data and UserTypeB data in separate tables so I can identify which user has which type.
Sorry if it's not clear this is my first question.
Thank you for your help!
In your apps main render method, you could do something like this.
Basically, you will listen to your redux state and switch main screen depending on the user type.
class MyApp extends PureComponent {
constructor(props) {
super(props);
}
render() {
const { auth } = this.props;
if (auth.userObj.type1) {
return <Type1MainComponent />;
}
if (auth.userObj.type2) {
return <Type2MainComponent />;
}
return <LoginScreen />;
}
}
function mapStateToProps(state) {
const { auth } = state;
return { auth };
}
export default connect(mapStateToProps)(MyApp);
For the life of me, I can't figure it out. All it shows is spinning without end and i am confused on the order of the life cycle happening. Basically, it goes to login or home screen and it works correctly on emulator but not on real device. I am on react 16.8.6 and react-native 0.60.5 environment.
I am getting started with RN and my debugging tools are not great. But for now just used Alert to see and the logic that was supposed to redirect to login/home screen is never reached. The Alerts shown are in the following order:
BS
mount2
render
mount1
My code is below: if the token exists, load home screen. else load auth screen is what I wanted to achieve but for now the line:
this.props.navigation.navigate(!goToLogin ? 'App' : 'Auth');
is never reached and so, spins a lot. Any help?
import React, {Component} from 'react';
import {StatusBar, View, Alert} from 'react-native';
import {
getUserToken,
loggedInToAssociation,
extractToken,
} from '../shared/loggedinUser';
import {setLanguage} from '../shared/localization';
import {appOptions} from '../config';
import Spinner from '../components/Spinner';
export default class AuthLoadingScreen extends Component {
constructor() {
super();
this.state = {
languageLoaded: false
};
}
componentDidMount() {
Alert.alert("mount1","oumnt1") // shown
loggedInToAssociation()
.then(details => {
// details is an array now
setLanguage(details['language']);
this.setState({languageLoaded: true});
Alert.alert("mount2","oumnt2") // SHOWN
})
.catch(err => {
setLanguage(appOptions.defaultLanguage);
this.setState({languageLoaded: true});
Alert.alert("mount3","oumnt3")
});
}
// Fetch the token from storage then navigate to our appropriate place
_bootstrapAsync = async () => {
const userToken = await getUserToken();
Alert.alert("bs","bs") // SHOWN
const tokenInfo = extractToken(userToken, 'both');
let goToLogin = true; // force user to go to the login page
if (tokenInfo.length == 2) {
goToLogin = false;
}
Alert.alert("bs2","bs2") // NEVER SHOWN
this.props.navigation.navigate(!goToLogin ? 'App' : 'Auth');
};
// Render any loading content that you like here
render() {
if (this.state.languageLoaded){
this._bootstrapAsync().then(s=>{
console.log(s)
}).catch(e=>{
console.log(e)
})
}
return (
<View>
<Spinner />
<StatusBar barStyle="default" />
</View>
);
}
}
did you check your debug console when running on device? There might be an unhandled promise rejection. The promise didn't go through but nowhere to handle the catch (consider try-catch scenario for this context).
It might be having a problem with this method.
extractToken(userToken, 'both')
I want to get the name of the current routeName in react navigator. I came across three solutions:
1. const { routeName } = navigation.state.routes[navigation.state.index];
2. this.props.navigation.state.RouteName
3. const route = navigationState.routes[navigationState.index];
The first one seems to work fine. For the second one, I am not sure how to use it. The third option (as given in official documentation), generates the error with
ReferenceError: navigationState is undefined.
Please help me out in which is the correct way to find the name of the active screen while navigation.
function getActiveRouteName(navigationState) {
if (!navigationState) {
return null
}
const route = navigationState.routes[navigationState.index]
if (route.routes) {
return getActiveRouteName(route)
}
return route.routeName
}
// Example Usage
const currentScreen = getActiveRouteName(this.props.router);
if (currentScreen === 'Login') {
// do something
}