I'm in a weird state where I'm trying to bug track an issue and output to the console log and I'm not even getting that at this point.
The post is asking for more details but there aren't any. I'm just trying to figure out basic bug tracking at this point.
import React, { Component } from 'react';
import { View } from 'react-native';
import firebase from 'firebase';
import { Header, Button, CardSection, Spinner } from './components/common/';
import LoginForm from './components/LoginForm';
class App extends Component {
state = { loggedIn: null };
componentWillMount() {
firebase.initializeApp({
apiKey: 'xxxxxxxxxxxx',
authDomain: 'xxxxxxxxx.firebaseapp.com',
databaseURL: 'https://xxxxxxx.firebaseio.com',
projectId: 'xxxxx',
storageBucket: 'xxxxx.appspot.com',
messagingSenderId: 'xxxxxxxxx'
});
firebase.auth().onAuthStateChanged((user) => {
if (user) {
this.setState({ loggedIn: true });
} else {
this.setState({ loggedIn: false });
}
});
}
renderContent() {
if (this.state.loggedIn) {
switch (this.state.loggedIn) {
case true:
return console.log('true');
// return (
// <CardSection>
// <Button onPress={() => firebase.auth().signOut()}>
// Log Out
// </Button>
// </CardSection>
// );
case false:
return console.log('false');
// return <LoginForm />;
default:
return console.log('default');
// return <Spinner size="large" />;
}
}
}
render() {
return (
<View>
<Header headerText="Authentication" />
{this.renderContent()}
</View>
);
}
}
export default App;
The problem is that this.state.loggedIn is null the whole time, therefore your code enters never the switch statements. Accordingly you don't get an output.
I just figured it out. It's the IF statement that's right after the renderContent call; it's not supposed to be there. Sorry to bother.
Related
I have following navigation stack
const AppNavigator = createStackNavigator({
AppSplashScreen: AppSplashScreen,
LanguageScreen: LanguageScreen,
WalkthroughScreen: WalkthroughScreen,
LoginScreen: LoginScreen,
ForgotPasswordScreen: ForgotPasswordScreen,
ResetPasswordScreen: ResetPasswordScreen,
RegistrationTypeScreen: RegistrationTypeScreen,
RegistrationFormScreen: RegistrationFormScreen,
OTPConfirmationScreen: OTPConfirmationScreen,
BottomTabNavigator: BottomTabNavigator
}, {
headerMode: 'none',
cardStyle: { backgroundColor: '#000000' },
});
const AppContainer = createAppContainer(AppNavigator);
export default App;
I am displaying splash screen video when the first app opens.
Here is what my AppSplashScreen looks like
import React, { Component } from 'react';
import { View } from 'react-native';
import SplashScreen from 'react-native-splash-screen';
import Video from 'react-native-video';
import { VIDEO_SPLASH_2 } from '../assets/videos/index';
export default class AppSplashScreen extends Component {
state = {
displayVideoPlayer: true,
firstLaunch: false
}
componentDidMount() {
SplashScreen.hide();
}
componentWillUnmount() {
this.setState({
displayVideoPlayer: false
});
}
isFirstLaunch() {
let firstLaunch = true;
if (true === storage.get('APP_ALREADY_LAUNCHED')) {
firstLaunch = false;
} else {
storage.set('APP_ALREADY_LAUNCHED', true);
firstLaunch = true;
}
return firstLaunch;
}
didCompleteVideoPlayback() {
if (true === this.state.displayVideoPlayer) {
this.setState({
displayVideoPlayer: false
});
}
const currentRouteName = this.props.navigation.state.routeName;
if ('AppSplashScreen' !== currentRouteName) {
return false;
}
if (true === global.SKIP_SPLASH_SCREEN_REDIRECT) {
return false;
}
if (this.isFirstLaunch()) {
this.props.navigation.navigate('LanguageScreen');
return false;
}
this.props.navigation.navigate('HomeScreen');
}
render() {
return (
<View style={{flex: 1, backgroundColor: '#000000', alignItems: 'center', justifyContent: 'center'}}>
{true === this.state.displayVideoPlayer &&
<Video
source={VIDEO_SPLASH_2}
muted={true}
repeat={false}
playInBackground={false}
resizeMode="contain"
onEnd={() => this.didCompleteVideoPlayback()}
style={{height: '100%', width: '100%', backgroundColor: '#000000'}}
/>
}
</View>
);
}
}
My issue is, whenever I put the application in Background, and resume after 30 seconds, it always starts with AppSplashScreen whereas I expect it to resume from the last screen. It works correctly if I open it before 30 seconds. I assume somewhere it is killing the memory and starting the app from start when I resume after 30 second.
What could be the issue here. Or is there another workaround to resume the app in the same screen where the user left off.
I solved it by using State Persistence of react-navigation
Here is the documentation https://reactnavigation.org/docs/4.x/state-persistence/
Here is what my App.js look like now
import AsyncStorage from '#react-native-community/async-storage';
const App: () => React$Node = () => {
const persistenceKey = "navigationStatePersistenceKey"
const persistNavigationState = async (navState) => {
try {
await AsyncStorage.setItem(persistenceKey, JSON.stringify(navState));
} catch(err) {
// handle error
}
}
const loadNavigationState = async () => {
const jsonString = await AsyncStorage.getItem(persistenceKey);
return JSON.parse(jsonString);
}
return(
<View style={{flex: 1, backgroundColor: '#000000'}}>
<AppContainer
persistNavigationState={persistNavigationState}
loadNavigationState={loadNavigationState}
/>
</View>
);
};
It now takes user to the same screen where it was left off, no more restart from first screen.
Is there a way I can fix this without using redux?
This is only happening on iOS, on android the AddListener works perfectly fine without it.
I have a component and I call the props.navigation.addListener on the componentDidMount functon.
Some code to help understand exactly where it breaks:
componentDidMount(){
var _this = this;
this.willBlurListener = this.props.navigation.addListener('willBlur', () => {
_this.timer.clearTimeout();
});
this.willFocusListener = this.props.navigation.addListener('willFocus', () => {
_this._action();
});
AppState.addEventListener('change', this._handleAppStateChange);
}
And then I use the component like this:
<Inactivity name='SomeNameView' navigation={ this.props.navigation }>
{this.renderDetails()}
</Inactivity>
Can you please try to use withNavigation function, it returns a HOC that has navigation in it props so you don't have to pass from the parent component to the child:
I created a simple app that uses this concept that probably can help you:
import React from 'react';
import {
View,
Text,
Button,
} from 'react-native';
import {
createStackNavigator,
withNavigation,
} from 'react-navigation';
class SomeComponent extends React.Component {
componentDidMount() {
this.willBlurListener = this.props.navigation.addListener('willBlur', () => {
this.someAction();
})
}
someAction() {
console.log('Some action is called!');
}
componentWillUnmount() {
this.willBlurListener.remove();
}
render() {
return (
<View>
<Text>Some Component</Text>
<Button
title={'Open settings'}
onPress={() => this.props.navigation.navigate('Settings')}
/>
</View>
)
}
}
const SomeComponentWithNavigation = withNavigation(SomeComponent);
class HomeScreen extends React.Component {
static navigationOptions = {
title: 'Home'
}
render() {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<SomeComponentWithNavigation/>
<Text>Welcome to home screen!</Text>
</View>
)
}
}
class SettingsScreen extends React.Component {
static navigationOptions = {
title: 'Settings'
}
render() {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>Welcome to settings screen!</Text>
</View>
)
}
}
export default createStackNavigator(
{
Home: HomeScreen,
Settings: SettingsScreen,
},
);
I have used import { useNavigation } from '#react-navigation/native'; to achieve this. This could work for you as well.
Sample code example
import { useNavigation } from '#react-navigation/native';
class CurrentOrderClass extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.onFocusSubscribe = this.props.navigation.addListener('focus', () => {
// Your code
});
}
componentWillUnmount() {
this.onFocusSubscribe();
}
.
.
.
.
function CurrentOrder(props) {
const navigation = useNavigation(props)
return <CurrentOrderClass {...props} navigation={navigation} />
}
}
export default CurrentOrder;
You can also check to React Native docs https://reactnavigation.org/docs/navigation-events/
I found this a bit tricky and after looking into it for a bit, I come up with the following solution. Note that is tested on React Navigation 5.x.
import { useIsDrawerOpen } from "#react-navigation/drawer";
let lastDrawerStateIsOpened = false;
const DrawerComponent = (props) => {
const isOpened = useIsDrawerOpen();
if (lastDrawerStateIsOpened != isOpened) {
lastDrawerStateIsOpened = isOpened;
if (isOpened) {
// Do what needs to be done when drawer is opened.
}
}
};
Also, note that I'm using a functional component.
I Have ScreenA To Click Next ScreenB Then back To Screen A Not Call Function componentWillMount()
ScreenA -> Next -> ScreenB -> Back() -> ScreenA
How to Reload Rout Screen in Back Action
Class ScreenA
import React from "react";
import { Button, Text, View } from "react-native";
class ScreenA extends Component {
constructor(props){
super(props)
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
})
}
}
componentWillMount() {
fetch(MYCLASS.DEMAND_LIST_URL, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
userId:'17'})
})
.then((response) => response.json())
.then((responseData) => {
if (responseData.status == '1') {
var data = responseData.data
this.setState({
dataSource: this.state.dataSource.cloneWithRows(data),
});
}
})
.done();
}
onPress = () => {
this.props.navigate("ViewB");
};
render() {
return (
<View>
<Text>test</Text>
<Button title="Next" onPress={this.onPress} />
</View>
);
}
}
Class ScreenB
import React from "react"
import { Button } from "react-native"
class ScreenB extends Component {
render() {
const {goBack} = this.props.navigation;
return(
<Button title="back" onPress={goBack()} />
)
}
}
Class ScreenA
import React from "react";
import { Button, Text, View } from "react-native";
class ScreenA extends Component {
constructor(props){
super(props)
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
})
}
}
componentWillMount() {
this.getData()
}
getData() {
fetch(MYCLASS.DEMAND_LIST_URL, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
userId:'17'})
})
.then((response) => response.json())
.then((responseData) => {
if (responseData.status == '1') {
var data = responseData.data
this.setState({
dataSource: this.state.dataSource.cloneWithRows(data),
});
}
})
.done();
}
onPress = () => {
this.props.navigate("ViewB", { onSelect: this.onSelect, getData: () => this.getData() });
};
render() {
return (
<View>
<Text>test</Text>
<Button title="Next" onPress={this.onPress} />
</View>
);
}
}
Class ScreenB
class ScreenB extends Component {
componentWillUnmount() {
this.props.navigation.state.params.getData()
}
render() {
const {goBack} = this.props.navigation;
return(
<Button title="back" onPress={goBack()} />
)
}
}
As react-navigation using stack. When we navigate to another screen, current screen remain as we have, another screen show on current screen. That means competent is still there. Component will reload (recycle) only if component creating again but at this point component will not change. We can reload data and re-render data.
By default react navigation not providing any api for onBack event. But we can achieve our goal by some tricks.
Way 1
use one function to handle onBack event and pass it to navigated screen
class ScreenA extends Component {
onBack() {
// Back from another screen
}
render() {
const { navigation } = this.props
return (
<Button title="Open ScreenB" onPress={() => navigation.navigate('ScreenB', { onBack: this.onBack.bind(this) })} />
)
}
}
// In this ScreenB example we are calling `navigation.goBack` in a function and than calling our onBack event
// This is not a safest as if any device event emmit like on android back button, this event will not execute
class ScreenB extends Component {
goBack() {
const { navigation } = this.props
navigation.goBack()
navigation.state.params.onBack(); // Call onBack function of ScreenA
}
render() {
return (
<Button title="Go back" onPress={this.goBack.bind(this)} />
)
}
}
// In this ScreenB example we are calling our onBack event in unmount event.
// Unmount event will call always when ScreenB will destroy
class ScreenB extends Component {
componentWillUnmount() {
const { navigation } = this.props
navigation.state.params.onBack();
}
render() {
return (
<Button title="Go back" onPress={() => this.props.navigation.goBack()} />
)
}
}
Way 2
Try react-navigation listener https://reactnavigation.org/docs/en/navigation-prop.html#addlistener-subscribe-to-updates-to-navigation-lifecycle
We have some limitation in this. We have blur and focus event. You can put your logic on focus. Whenever you will back from another screen, ScreenA will focus and we can execute our logic. But there is one issue, this will execute every time when we got focus whatever first time or we minimize and reopen application.
Way 3
https://github.com/satya164/react-navigation-addons#navigationaddlistener
I'm not sure about this way, I didn't tried.
[
the image is the resultof when you console.log(this.state.fixtures)
I havent run into an error, but also havent gotten a result. Just trying to pass the individual match, from the .map(), to the Card component. Not sure if the onPress should be called in the TouchableOpacity. Been looking at this so a couple of day, any feedback s appreciated. Thank You.
import React, { Component } from 'react';
import { View, Text, TouchableOpacity, LayoutAnimation } from 'react-native';
import { Card, CardSection } from '../common';
//import ListOf from './ListOf';
export default class LeagueCard extends Component {
state ={
fixtures: null
}
componentDidMount = () => {
const {league_name, league_id } = this.props.league
{this.getMatches(league_id)}
}
getMatches = (league_id) => {
let legaueID = league_id
let fixArray = []
//console.log(legaueID)
fetch(`https://apifootball.com/api/?action=get_events&from=2016-10-30&to=2016-11-01&league_id=${legaueID}&APIkey=42f53c25607596901bc6726d6d83c3ebf7376068ff89181d25a1bba477149480`)
.then(res => res.json())
.then(fixtures => {
fixtures.map(function(fix, id){
fixArray =[...fixArray, fix]
})
this.setState({
fixtures: fixArray
})
console.log(this.state.fixtures)
})
}
display = () => {
//console.log(this.state.fixtures)
if(this.state.fixtures != null){
this.state.fixtures.map(function(match, id){
//console.log(match)
return (
<Text>match</Text>
)
})
}
}
render(){
const {league_name, league_id } = this.props.league
return(
<View>
<TouchableOpacity
onPress={() => this.display()}
>
<Card>
<CardSection>
<Text>{league_name}</Text>
</CardSection>
</Card>
</TouchableOpacity>
</View>
)}
}
enter code here
import React, {Component} from 'react';
import {View, Text, TouchableOpacity, LayoutAnimation} from 'react-native';
import {Card, CardSection} from '../common';
export default class LeagueCard extends Component {
constructor(props) {
super(props);
this.state = {
fixtures: null,
matches: []
}
}
componentDidMount() {
const {league_name, league_id} = this.props.league;
this.getMatches(league_id)
};
getMatches = (league_id) => {
let legaueID = league_id;
let fixArray = [];
fetch(`https://apifootball.com/api/?action=get_events&from=2016-10-30&to=2016-11-01&league_id=${legaueID}&APIkey=42f53c25607596901bc6726d6d83c3ebf7376068ff89181d25a1bba477149480`)
.then(res => res.json())
.then(fixtures => {
fixtures.map(function (fix, id) {
fixArray = [...fixArray, fix]
});
this.setState({
fixtures: fixArray
})
})
};
display = () => {
if (this.state.fixtures != null) {
this.setState({
matches: this.state.fixtures.map(match => <Text>{match.country_name}</Text>)
});
}
};
render() {
const {league_name, league_id} = this.props.league;
return (
<View>
<TouchableOpacity onPress={this.display}>
<Card>
<CardSection>
{this.state.matches}
</CardSection>
</Card>
</TouchableOpacity>
</View>
)
}
}
I made the changes I think will render your matches for you. I made your display() f set the state.matches of your LeagueCard, which will now be an array of Text components each displaying match. The Array.prototype.map function in JavaScript returns a new array which should then be used.
Also should mention that I added a constructor where I initialize state, though that is not strictly necessary it is a good practice.
Watch out for typos too, you have one in getMatches which I did not fix.
Edit: I changed match to match.country_name as you can't give objects directly to a Text component. You will need to grab each key/value pair of the object you want to display.
this will not show any error to you, but your components will not render, because react will not know where to render it, a workaround to this problem is try something like this
import React, { Component } from 'react';
import { View, Text, TouchableOpacity, LayoutAnimation } from 'react-native';
import { Card, CardSection } from '../common';
//import ListOf from './ListOf';
export default class LeagueCard extends Component {
state ={
fixtures: null,
showTextList: false
}
componentDidMount = () => {
const {league_name, league_id } = this.props.league
{this.getMatches(league_id)}
}
getMatches = (league_id) => {
let legaueID = league_id
let fixArray = []
//console.log(legaueID)
fetch(`https://apifootball.com/api/?action=get_events&from=2016-10-30&to=2016-11-01&league_id=${legaueID}&APIkey=42f53c25607596901bc6726d6d83c3ebf7376068ff89181d25a1bba477149480`)
.then(res => res.json())
.then(fixtures => {
fixtures.map(function(fix, id){
fixArray =[...fixArray, fix]
})
this.setState({
fixtures: fixArray
})
})
}
display = () => {
//console.log(this.state.fixtures)
this.setState({showTextList: true})
}
render(){
const {league_name, league_id } = this.props.league
return(
<View>
<TouchableOpacity
onPress={() => this.display()}
>
<Card>
<CardSection>
<Text>{league_name}</Text>
{this.state.showTextList && this.state.fixtures &&
this.state.fixtures.map((match, id) => (<Text>{match}</Text>))
}
</CardSection>
</Card>
</TouchableOpacity>
</View>
)}
}
I just put the text list inside your CardSection, because i belive you want to render the list inside it,but feel free to put this wherewver you want
I am new to React Native and trying to create a simple iOS app. The app has a button on clicking which I need to store the timestamp of the click on the device in a file.
I know that React Native has an API called AsyncStorage but I am getting errors while using this. I copied the code from some site on the net.
Can someone please guide me to use this API?
This is my entire code:
import React, {Component} from 'react';
import { StyleSheet, Text, View, TextInput, AsyncStorage } from 'react-native';
export default class App extends Component{
state = {
'name': ''
}
componentDidMount = () => AsyncStorage.getItem('name').then((value) => this.setState({'name': value}))
setName = (value) => {
AsyncStorage.setItem('name': value);
this.setState({'name': value});
}
render() {
return (
<View style={styles.container}>
<TextInput style = {styles.textInput} autoCapitalize = 'none'
onChangeText = {this.setName}/>
<Text>
{this.state.name}
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
marginTop: 50
},
textInput: {
margin: 15,
height: 35,
borderWidth: 1,
backgroundColor: '#7685ed'
}
});
As for the error, when I launch the code on iOS, I am getting a red screen. There is no syntax error that I can see.
Thanks in advance.
Hard to say without more detail the exact problem you're facing, but I assume some of the following might help you?
Ah I see you posted some code. You will need a constructor that defines your state as well. Added it in my code below.
Please note I'm not an expert. Forgive any errors
import {
AsyncStorage,
} from 'react-native';
class myComponent extends React.Component{
constructor(props) {
super(props);
this.state = {
data: null
};
}
componentDidMount() {
this._loadInitialState().done();
}
_someFunction() {
var myData = 123;
saveItemLocally('data', myData);
}
async _loadInitialState() {
try {
// get localy stored data
var dataStored = await AsyncStorage.getItem('data');
if (dataStored!==null) {
this.setState({
data: dataStored
});
}
} catch (error) {
//didn't get locally stored data
console.log(error.message);
}
} // end _loadinitialstate
render () {
//your render function
return (
);
}
} // end of your component
async function saveItemLocally(item, value) {
try {
await AsyncStorage.setItem(item, value);
} catch (error) {
console.log('AsyncStorage error: ' + error.message);
}
}