React Native - Login with React Navigation - reactjs

I'm struggling a bit with login functionality in my react native app.
I'm using the facebook-login, and it is working. But what to do after successful login, is where I'm struggling.
I've tried this:
To have two navigators in App.js. One for the login, and one for all the rest. In the app.js, I have a loggedIn-state which is false by default. Then when the login is successful, I want to change the loggedIn-state to true, and swap to the other navigator. But I'm unable to sending a prop from the LoginStack-navigator, to the App.js-component, so I'm not able to change the state in App.js
This is the code I'm having now:
// App.js
const RootStack = createStackNavigator({
Single: Single,
Search: Search,
Home: Homepage,
TagsSingle: TagsSingle
},
{
initialRouteName: 'Home',
drawerBackgroundColor: '#2D2D2D'
});
const LoginStack = createStackNavigator({
Login: Login,
},
{
drawerBackgroundColor: '#2D2D2D',
});
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
loggedIn: false
}
}
handleLogin = () => {
console.log("Test");
this.setState({loggedIn: true});
}
render() {
return (
<View style={{flex:1}}>{this.state.loggedIn ? <RootStack /> : <LoginStack handleLogin={this.handleLogin} />}</View>
);
}
}
// Login.js - where the login happens
class Login extends React.Component {
constructor(props){
super(props);
this.state = {
message: 'Logg inn med facebook'
}
}
_fbLogin = (ev) => {
LoginManager.logInWithReadPermissions(['public_profile']).then(function(result){
if(result.isCancelled){
console.log('Login cancelled');
}
else{
console.log('Login succes ' + result.grantedPermissions);
ev.setState({message: 'Logget inn'});
ev.props.handleLogin(); // creates error
}
}, function(error){
console.log("An error occured:");
console.log(error);
});
}
render() {
return (
<View style={{ flex:1, backgroundColor: '#2D2D2D', justifyContent: 'center', alignItems: 'center' }}>
<View>
<TouchableOpacity onPress={() => {this._fbLogin(this)}}>
<Text style={{color:'#fff'}}>{this.state.message}</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
export default Login;
But nothing happens in the HandleLogin-function in App.js, and I'm getting an error saying 'ev.props.handleLogin is not a function' (I've also tried calling the prop without '()', which gives no error, but still no reaction).
How can I solve this? Also, am I on the right track here, with the two navigators?

Try this example from their documentation.

Related

Opening I frame in react-native

I'm integrating stripe payment in react-native and wants to open an iframe with specific url from after buttton click.
Scenario : user will enter the card details, details will be given to api end point and it will return an url that will contain the authentication part like OTP. So I want that url to be opened how to do so ? Let me if is there any better way to open that authentication middleware.
Adding code below
Payment.js
import React, { Component } from 'react';
import { View, Button } from 'react-native';
import stripe from 'tipsi-stripe';
import { doPayment } from '../api/api';
import { Auth } from './Auth';
stripe.setOptions({
publishableKey: 'pk_test_********',
});
export default class Payment extends Component {
state = {
isPaymentPending: false
}
requestPayment = () => {
this.setState({ isPaymentPending: true });
return stripe
.paymentRequestWithCardForm()
.then(stripeTokenInfo => {
return doPayment(10330, stripeTokenInfo.tokenId);
})
.then((res) => {
let url = "<iFrame src='" + res.intent_url + "' />"
console.log(res, url);
openAuthentication(url); --->>>> here i'm calling a function with url
})
.catch(error => {
console.warn('Payment failed', { error });
})
.finally(() => {
this.setState({ isPaymentPending: false });
});
};
render() {
return (
<View style={styles.container}>
<Button
title="Make a payment"
onPress={this.requestPayment}
disabled={this.state.isPaymentPending}
/>
</View>
);
}
}
openAuthentication = (url) => {
console.log("Here with props :::::::", url);
// here I want to open an iframe, i'm getting correct url, I've checked it in a static html page and it is working
<Auth url={url} />
}
const styles = {
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
};
Auth.js
import React, { Component } from 'react'
import {
View, StyleSheet
} from 'react-native'
import { WebView } from 'react-native-webview'
export default class Auth extends Component {
constructor(props) {
super(props)
console.log(props, ">>>>>>>>>>>")
}
render() {
console.log("In Auth -----------");
return (
<View style={styles.container}>
<WebView
source={{ uri: 'myUrl' }}
/>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
}
})
Error:
React.createElement type is invalid, expected a string or a class/function.
Auth needs to be returned by a render function, otherwise nothing will show up.
So, you'll want something similar to this:
render() {
return (
<View style={styles.container}>
<Button
title="Make a payment"
onPress={this.requestPayment}
disabled={this.state.isPaymentPending}
/>
{this.state.url && (
<Auth url={this.state.url} />
)}
</View>
);
}
You only want to render auth when you have the url.
For this, you want to update your state to look something like this:
state = {
isPaymentPending: false,
url: undefined
}
And
.then((res) => {
let url = "<iFrame src='" + res.intent_url + "' />";
this.setState({ url });
})
In order to update you state with the received url when the promise resolves. This will update your state, set the url, and re-render. Because you have an url, Auth should be rendered as well.
LE:
Your Auth.js should look something like this in order to be able to display static HTML. this.props.url should be valid HTML.
render() {
console.log("In Auth -----------");
return (
<View style={styles.container}>
<WebView
source={{ html: this.props.url }}
originWhitelist={['*']}
/>
</View>
);
}

Refresh screen or component when navigate to it

I have two screens, one for displaying the records consuming an API and the other for registering.
the problem is that when I do a register and navigate to the display screen it does not update.
This is a construction of the screen:
constructor(props) {
super(props);
this.state = {isLoading: true, pendIsLoading: true, dataSource: [], contentStorageS:""}
};
fetchDados = async () => {
let usuario = await AsyncStorage.getItem("ASCOFAR_app_usuario");
try {
const response = await api.get("api/listaRegistros.php?usuario="+usuario);
const responseData = await response.data
if(responseData.status == "ERRO"){
this.setState({
isLoading: false,
dataSource: "",
})
}else{
this.setState({
isLoading: false,
dataSource: responseData,
})
}
console.log(response)
} catch (error) {
Alert.alert(error)
}
}
async componentDidMount () {
this.fetchDados();
this.atualizaState();
}
tirarLoad() {
if(this.state.isLoading == true){
return (
<ActivityIndicator size="large" color="#be152c"/>
)
}else if(this.state.dataSource == ""){
return (
<ScrollView >
<View style={{justifyContent:"center", alignItems:"center",}}>
<Image
style ={{width:150, height:150, marginTop:35}}
source={require('../assets/images/aguardando.png')}
/>
</View>
</ScrollView>
)
}else{
return (
<ScrollView>
<Text style={styles.tituloGrid}>Formularios Enviados</Text>
{this.state.dataSource.map(dados => (
<View style={styles.list} key={dados.id}>
<Text style={styles.listChild}>{dados.id}</Text>
<Text style={styles.listChild}>{dados.nome}</Text>
<Text>|</Text>
<Text style={styles.listChild}>{dados.endereco}</Text>
</View>
))}
</ScrollView>
)
}
}
<View style={styles.grid}>
{this.tirarLoad()}
</View>
I need to know how to do when navigating to this screen to update API consumption
Assuming you are using React-Navigation, did you try to addListener
focus react-navigation documentation
You could also do it by componentDidUpdate. I could not find the official documentation for doing it on 5.x. I believe it still works with 5.x. (Doc on 3.x)
import { withNavigationFocus } from "react-navigation";
componentDidUpdate(prevProps) {
if (prevProps.isFocused !== this.props.isFocused) {
this.fetchDados()
//or other similar onFocus function
}
}
export default withNavigationFocus(TabScreen);
Try re-rendering your Home screen after navigation
this.props.navigation.navigate('Home', {
onBack: () => this.refresh() //function to refresh screen,
});
import { withNavigationFocus } from "react-navigation";
this.willFocusSubscription = this.props.navigation.addListener(
'willFocus',
() => {
this.refreshFetch();
this.refreshLocal();
}
);
componentWillUnmount() {
this.willFocusSubscription.remove();
}

Can I subscribe to this.props.navigation.state.params?

I am wonder if in screenA I have an object data = {} that will be changed dynamically, can I receive changes in screenB by just sending this props from screenA through this.props.navigation.navigate('screenB', {data})?
And in screenB to have a componentWillReceiveProps(nextProps) to get this changes through something like nextProps.navigation.state.param.data
Or there is a way to achieve this?
You can use onWillFocus of NavigationEvents, which fires whenever the screen is navigated to.
_willFocus = () => {
const { navigation } = this.props
const data = navigation.getParam('data', null)
if (data !== null) {
/* do something */
}
}
/* ... */
render () {
return (
<View>
<NavigationEvents onWillFocus={_willFocus()}
</View>
)
}
It is easy, just as you said: send some data navigation.navigate('screenB', { data }) and receive it in the screenB as navigation.state.params.data.
I agree with #FurkanO you probably show use Redux instead to control all the state of your app, but for simple stuff I think isn't necessary!
I made a simple snack demo to show you: snack.expo.io/#abranhe/stackoverflow-56671202
Here some code to follow up:
Home Screen
class HomeScreen extends Component {
state = {
colors: ['red', 'blue', 'green'],
};
render() {
return (
<View>
{this.state.colors.map(color => {
return <Text>{color}</Text>;
})}
<View>
<Text>Details Screen</Text>
<Button
title="Go to Details"
onPress={() => this.props.navigation.navigate('Details', { colors: this.state.colors })}
/>
</View>
</View>
);
}
}
Details Screen
class DetailsScreen extends Component {
state = {
colors: [],
};
componentWillMount() {
this.setState({ colors: this.props.navigation.state.params.colors });
}
render() {
return (
<View>
{this.state.colors.map(color => {
return <Text>{color}</Text>;
})}
<Text>Details Screen</Text>
</View>
);
}
}
Update
The question's author requested an update to add a setTimeout() to see the exact moment when the data is on the other screen, so it will look like this:
componentWillMount() {
setTimeout(() => {
this.setState({ colors: this.props.navigation.state.params.colors });
}, 3000);
}

Update tabBarIcon in navigationOptions using state, not working

I am trying to change the badge of the tabNavigator from navigationOptions dynamically in React Native.
I am trying to change it with setState, but it is not updating.
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
postsBadgeCount: 0,
progressBadgeCount: 0
};
this.Apps = this.startApp();
}
componentDidMount() {
setInterval(() => {
this.setState({ progressBadgeCount: this.state.progressBadgeCount + 1 });
console.log('change state', this.state.progressBadgeCount);
}, 5000);
}
startApp() {
const BottomNav = createMaterialBottomTabNavigator({
Progress: {
screen: stack2,
navigationOptions: {
tabBarLabel: 'Progress',
tabBarIcon: ({ tintColor }) => (
<View>
<IconFA name="calendar-check-o" size={22} color={tintColor} />
{this.state.progressBadgeCount > 0 ?
<View style={style.badge}>
<Text style={style.badgeCount}>1</Text>
</View> : undefined}
</View>
)
}
},
{
...
});
const navigator = createSwitchNavigator(
...
);
return createAppContainer(navigator);
}
render() {
return (
<this.Apps/>
);
}
}
When I am trying to update the progressBadgeCount using setState, it doesn't change on UI.
Any suggestions on how to update the badge count in this situation?
Thank you
startApp() is running only once, in the constructor. Rather than saving it into this.Apps, you could change your render() to:
render() {
return this.startApp();
}
This way it runs startApp() on each rerender.

how do I dynamically render title as component

I have a scene:
<Scene key="myFeed" component={myFeed} renderTitle={level}/>
In which I use the component to display the "title".
This function:
const level = () => {
return (
<Level/>
)
}
This component:
export class Level extends Component {
constructor(props) {
super(props);
this.state = {
progressBar: {},
loading: true,
};
}
connection() {
myApi.getServer('/user/level/', (data) => {
this.setState({loading: false, progressBar: data});
}, (err) => {
console.log(err.message);
this.setState({loading: false});
})
}
render() {
return(
<View style={styles.topBarCenterSlider}>
<View style={styles.topBarSlider}>
<View style={[styles.topBarStatus, {width: this.state.progressBar.level["progress"] + '%' : 0}]}/>
</View>
</View>
</View>
);
}
}
I need to change the component after the server response.
Help please, and sorry for my English))
I see you dont invoke "connection()". Put in componentWiLlMount
componentWillMount() {
this.connection();
}

Resources