i am using redux in my react native app .
my problem is how to show the error message that appear in the terminal to the render .
i tried to use {this.props.error} and nothing will show up
Actions
import * as actionTypes from "./Types";
export const setErrors = errors => ({
type: actionTypes.SET_ERRORS,
payload: errors
});
export const resetError = () => {
return async dispatch => {
dispatch({
type: actionTypes.RESET_ERROR,
payload: []
});
};
};
Reducers
import * as actionTypes from "../actions/Types";
import { SET_ERRORS, RESET_ERROR } from "../actions/Types";
const initialState = {
errors: [],
reset: ""
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case SET_ERRORS:
console.log("from reducer");
console.log("errrrro", action.payload);
return {
...state,
errors: Object.keys(action.payload).map(
key => `${key}: ${action.payload[key]}`
)
};
case RESET_ERROR:
return {
...state,
errors: action.payload
};
default:
return state;
}
};
export default reducer;
this is my index.js
import React, { Component } from "react";
import { connect } from "react-redux";
import * as actionCreators from "../../store/actions";
import {
StyleSheet,
Text,
View,
TextInput,
Button,
TouchableHighlight,
Image
} from "react-native";
import { LinearGradient } from "expo-linear-gradient";
class Login extends Component {
state = {
username: "",
password: ""
};
resetState = () => {
this.setState({ password: null });
};
errors = () => {
this.props.errors;
};
Login = () => {
this.props.login(this.state, this.props.navigation);
this.resetState();
};
render() {
return (
<LinearGradient
colors={["#002d44", "#001724"]}
style={{ flex: 1 }}
start={[0, 0]}
end={[1, 0]}
>
<View style={styles.container}>
<Text>{error}</Text>
<View style={styles.inputContainer}>
<Image
style={styles.inputIcon}
source={{
uri:
"https://img.icons8.com/ios/80/000000/identification-documents-filled.png"
}}
/>
<TextInput
style={styles.inputs}
autoCapitalize="none"
placeholder="اسم المستخدم"
onChangeText={username => this.setState({ username })}
value={this.state.username}
/>
</View>
<View style={styles.inputContainer}>
<Image
style={styles.inputIcon}
source={{
uri: "https://img.icons8.com/ios/80/000000/lock-2-filled.png"
}}
/>
<TextInput
style={styles.inputs}
placeholder="كلمة المرور"
secureTextEntry={true}
onChangeText={password => this.setState({ password })}
value={this.state.password}
/>
</View>
<TouchableHighlight
style={[styles.buttonContainer, styles.loginButton]}
onPress={this.Login}
>
<Text style={{ color: "#fff", fontSize: 15 }}>دخول</Text>
</TouchableHighlight>
<TouchableHighlight
style={{}}
onPress={() => this.props.navigation.replace("Signup")}
>
<Text style={{ color: "#00F7EA", fontSize: 15 }}>تسجيل</Text>
</TouchableHighlight>
</View>
</LinearGradient>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center"
},
inputContainer: {
borderBottomColor: "#F5FCFF",
backgroundColor: "#FFFFFF",
borderRadius: 30,
borderBottomWidth: 1,
width: 250,
height: 45,
marginBottom: 20,
flexDirection: "row",
alignItems: "center"
},
inputs: {
height: 45,
marginLeft: 16,
fontSize: 15,
textAlign: "center",
borderBottomColor: "#FFFFFF",
flex: 1
},
inputIcon: {
width: 30,
height: 30,
marginLeft: 15,
justifyContent: "center"
},
buttonContainer: {
height: 45,
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
marginBottom: 20,
width: 250,
borderRadius: 30,
borderWidth: 0.8,
borderColor: "#d6d7da"
},
loginButton: {
backgroundColor: "transparent"
},
buttonDisabled: {
backgroundColor: "#e7e7e7"
},
loginText: {
color: "white"
}
});
const mapStateToProps = state => ({
user: state.authReducer.user,
loading: state.devicesReducer.loading,
errors: state.errorReducer.errors
});
const mapDispatchToProps = dispatch => ({
login: (userData, navigation) =>
dispatch(actionCreators.login(userData, navigation)),
checkForExpiredToken: navigation =>
dispatch(actionCreators.checkForExpiredToken(navigation))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(Login);
the error message that showing on the terminal
from reducer
errrrro Object {
"password": Array [
"This field may not be blank.",
],
"username": Array [
"This field may not be blank.",
],
}
from reducer
errrrro [Error: Request failed with status code 400]
Related
I'm transitionning from React to React Native, and so far got the basics. In my application I'm trying to implement the authentification.
I already setup my working APIs for the login and registration and verified them using POSTMAN.
The problem here is for some reason onPress={onSignInHandler} doesn't do anything. Normally in React I would put onClick={onSignInHandler} and everything will be working fine.
I would like, when the Login is pressed, execute the axios request, and then redirect to for example "Myprofil.js" component or atleast display and alert to see if it's working. As far as I know React Native uses react-navigation instead of react-router.
app.js:
import * as React from 'react';
import { View, Text } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import Login from './src/views/Login';
import Signup from './src/views/Signup ';
import MyProfil from './src/views/MyProfil';
const Stack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Login" screenOptions={{
headerShown: false
}}>
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Signup " component={Signup} />
<Stack.Screen name="MyProfil" component={MyProfil} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
login.js :
import React, { Component, UseState } from 'react';
import { View, Text, TextInput, StyleSheet, Image, Dimensions, Alert } from 'react-native';
import axios from "axios";
const { width, height } = Dimensions.get('window');
function Login() {
const [state, setState] = React.useState({
msg: "",
isLoading: false,
redirect: false,
errMsgEmail: "",
errMsgPwd: "",
errMsg: "",
});
const [email, setEmail] = React.useState("");
const [password, setPassword] = React.useState("");
const handleChangeEmail = (event) => {
setEmail(event.target.value);
};
const handleChangePassword = (event) => {
setPassword(event.target.value);
};
function onSignInHandler() {
const infos = {
email: email,
password: password,
};
axios
.post("http://127.0.0.1:8000/api/login", infos)
.then((response) => {
if (response.data.status === 200) {
localStorage.setItem("isLoggedIn", true);
localStorage.setItem("userData", JSON.stringify(response.data.data));
this.setState({
msg: response.data.message,
redirect: true,
});
}
if (
response.data.status === "failed" &&
response.data.success === undefined
) {
this.setState({
errMsgEmail: response.data.validation_error.email,
errMsgPwd: response.data.validation_error.password,
});
setTimeout(() => {
this.setState({ errMsgEmail: "", errMsgPwd: "" });
}, 2000);
} else if (
response.data.status === "failed" &&
response.data.success === false
) {
this.setState({
errMsg: response.data.message,
});
setTimeout(() => {
this.setState({ errMsg: "" });
}, 2000);
}
})
.catch((error) => {
console.log(error);
});
};
return (
<View
style={{
flex: 1,
backgroundColor: 'white',
justifyContent: 'flex-end'
}}
>
<View style={{ ...StyleSheet.absoluteFill }}>
<Image
source={require('../images/bg.jpg')}
style={{ flex: 1, height: null, width: null }}
>
</Image>
</View>
<View style={{ height: height / 2, justifyContent: 'center' }}>
<View>
<Text style={{ fontSize: 20, fontWeight: 'bold', color:"#ffffff",fontSize: 30, marginHorizontal: 40,marginVertical: 10, }}>Bienvenue !</Text>
</View>
<View style={styles.button}>
<TextInput placeholder="E-mail " style={{ fontSize: 20, fontWeight: 'bold' }} value={email} onChange={handleChangeEmail}></TextInput>
</View>
<View style={styles.button}>
<TextInput placeholder="Password" style={{ fontSize: 20, fontWeight: 'bold' }} secureTextEntry={true} value={password} onChange={handleChangePassword}></TextInput>
</View>
<View style={{alignItems: 'center',justifyContent: 'center', marginTop: 20}}>
<Text style={{ fontSize: 20, fontWeight: 'bold', color: '#ffffff' }} onPress={onSignInHandler}>
LOGIN
</Text>
</View>
</View>
</View>
);
}
export default Login;
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
button: {
backgroundColor: 'white',
height: 50,
marginHorizontal: 30,
borderRadius: 35,
alignItems: 'center',
justifyContent: 'center',
marginVertical: 10,
}
});
Instead of using onPress for Text component wrap it with a TouchableOpacity and use the onPress of the TouchableOpacity
<TouchableOpacity onPress={onSignInHandler}>
<Text style={{ fontSize: 20, fontWeight: 'bold', color: '#ffffff' }} title="Profil" >LOGIN</Text>
</TouchableOpacity>
Change your
function onSignInHandler() {
to
const onSignInHandler = () => {
I am trying to develop an application to learn react native and I am stuck with the login/register.
I have a register.js that works properly, as I have tested it before, and I wanted to add a resiter button to redirect to the register page from the login one.
From the UserLogin.js, the button is this one:
<TouchableOpacity
onPress={() => navigate('Register')}
style={{width:200,padding:10,backgroundColor:'pink',alignItems:'center'}}>
<Text style={{color:'black'}}>Register</Text>
</TouchableOpacity>
UserLogin.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,TouchableOpacity,TextInput,Button,Keyboard
} from 'react-native';
import { StackNavigator } from 'react-navigation';
class UserLogin extends React.Component {
constructor(props){
super(props)
this.state={
userEmail:'',
userPassword:''
}
}
login = () =>{
const {userEmail,userPassword} = this.state;
let reg = /^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/ ;
if(userEmail==""){
//alert("Please enter Email address");
this.setState({email:'Please enter Email address'})
}
else if(reg.test(userEmail) === false)
{
//alert("Email is Not Correct");
this.setState({email:'Email is Not Correct'})
return false;
}
else if(userPassword==""){
this.setState({email:'Please enter password'})
}
else{
fetch('http://192.168.1.47/login.php',{
method:'post',
header:{
'Accept': 'application/json',
'Content-type': 'application/json'
},
body:JSON.stringify({
// we will pass our input data to server
email: userEmail,
password: userPassword
})
})
.then((response) => response.json())
.then((responseJson)=>{
if(responseJson == "ok"){
// redirect to profile page
alert("Successfully Login");
//this.props.navigation.navigate("Profile");
}else{
alert("Wrong Login Details");
}
})
.catch((error)=>{
console.error(error);
});
}
Keyboard.dismiss();
}
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.container}>
<Text style={{padding:10,margin:10,color:'red'}}>{this.state.email}</Text>
<TextInput
placeholder="Enter Email"
style={{width:200, margin:10}}
onChangeText={userEmail => this.setState({userEmail})}
/>
<TextInput
placeholder="Enter Password"
style={{width:200, margin:10}}
onChangeText={userPassword => this.setState({userPassword})}
/>
<TouchableOpacity
onPress={this.login}
style={{width:200,padding:10,backgroundColor:'magenta',alignItems:'center'}}>
<Text style={{color:'white'}}>Login</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => navigate('Register')}
style={{width:200,padding:10,backgroundColor:'pink',alignItems:'center'}}>
<Text style={{color:'black'}}>Register</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
});
export default UserLogin
AppRegistry.registerComponent('login', () => login);
register.js
import React, { Component } from 'react';
import { View, Text, StyleSheet, TextInput, TouchableOpacity, ActivityIndicator, Platform } from 'react-native';
class Register extends React.Component
{
constructor()
{
super();
this.state = { first_name: '', last_name: '', email: '', password: '', loading: false, disabled: false }
}
saveData = () =>
{
this.setState({ loading: true, disabled: true }, () =>
{
fetch('http://192.168.1.47/user_registration.php',
{
method: 'POST',
headers:
{
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
first_name: this.state.first_name,
last_name: this.state.last_name,
email: this.state.email,
password: this.state.password,
})
}).then((response) => response.json()).then((responseJson) =>
{
//Faire une redirection evrs le profile de l'utilisateur
alert(responseJson);
this.setState({ loading: false, disabled: false });
}).catch((error) =>
{
console.error(error);
this.setState({ loading: false, disabled: false });
});
});
}
render()
{
return(
<View style = { styles.container }>
<TextInput underlineColorAndroid = "transparent" placeholder = "Your First Name" style = { styles.textInput } onChangeText = {(text) => this.setState({ first_name: text })}/>
<TextInput underlineColorAndroid = "transparent" placeholder = "Your Last Name" style = { styles.textInput } onChangeText = {(text) => this.setState({ last_name: text })}/>
<TextInput underlineColorAndroid = "transparent" placeholder = "Your Email adress" style = { styles.textInput } onChangeText = {(text) => this.setState({ email: text })}/>
<TextInput underlineColorAndroid = "transparent" placeholder = "Your Password" style = { styles.textInput } onChangeText = {(text) => this.setState({ password: text })}/>
<TouchableOpacity disabled = { this.state.disabled } activeOpacity = { 0.8 } style = { styles.Btn } onPress = { this.saveData }>
<Text style = { styles.btnText }>Sign up</Text>
</TouchableOpacity>
{
(this.state.loading)
?
(<ActivityIndicator size = "large" />)
:
null
}
</View>
);
}
}
const styles = StyleSheet.create(
{
container:
{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#eee',
paddingHorizontal: 25,
paddingTop: (Platform.OS == 'ios') ? 20 : 0
},
textInput:
{
height: 40,
borderWidth: 1,
borderColor: 'grey',
marginVertical: 5,
alignSelf: 'stretch',
padding: 8,
fontSize: 16
},
Btn:
{
backgroundColor: 'rgba(0,0,0,0.6)',
alignSelf: 'stretch',
padding: 10,
marginTop: 10,
marginBottom: 25
},
btnText:
{
textAlign: 'center',
color: 'white',
fontSize: 16
}
});
export default Register
Navigation.js
import Search from '../Components/Search';
import ProductDetail from '../Components/ProductDetail';
//import UserAccount from '../Components/UserAccount';
import UserLogin from '../Components/UserLogin';
//import UserRegistration from '../Components/UserRegistration';
import Transaction from '../Components/Transaction';
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import React from 'react';
import { Image , Text, TouchableOpacity } from 'react-native';
const SearchStackNavigator = createStackNavigator({
Rechercher : {
screen: Search,
navigationOptions: ({ navigate, navigation }) => ({
title:'Rechercher',
headerTitleStyle: {
fontWeight: 'bold',
color: 'white',
marginLeft:5,
},
headerStyle: {
backgroundColor: '#FF6100',
},
headerLeft: (
<TouchableOpacity
onPress={() => navigation.navigate('Rechercher')}>
<Image
style={{
width: 35,
height: 35,
marginLeft: 25,
}}
source={require('../Images/logo.jpg')}
/>
</TouchableOpacity>
),
headerRight: (
<TouchableOpacity
onPress={() => navigation.navigate('Account')}>
<Image
style={{
width: 35,
height: 35,
marginRight: 25,
}}
source={require('../Images/user.png')}
/>
</TouchableOpacity>
)
})
},
ProductDetail : {
screen: ProductDetail,
navigationOptions: ({ navigate, navigation }) => ({
title:'Détail du produit',
headerTitleStyle: {
fontWeight: 'bold',
color: 'white',
marginLeft:5,
},
headerStyle: {
backgroundColor: '#FF6100',
},
headerLeft: (
<TouchableOpacity
onPress={() => navigation.navigate('Rechercher')}>
<Image
style={{
width: 35,
height: 35,
marginLeft: 25,
}}
source={require('../Images/logo.jpg')}
/>
</TouchableOpacity>
),
headerRight: (
<TouchableOpacity
onPress={() => navigation.navigate('Account')}>
<Image
style={{
width: 35,
height: 35,
marginRight: 25,
}}
source={require('../Images/user.png')}
/>
</TouchableOpacity>
)
})
},
Account: {
//screen: UserRegistration,
screen: UserLogin,
navigationOptions: ({ navigate, navigation }) => ({
title:'Compte utilisateur',
headerTitleStyle: {
fontWeight: 'bold',
color: 'white',
marginLeft:5,
},
headerStyle: {
backgroundColor: '#FF6100',
},
headerLeft: (
<TouchableOpacity
onPress={() => navigation.navigate('Rechercher')}>
<Image
style={{
width: 35,
height: 35,
marginLeft: 25,
}}
source={require('../Images/logo.jpg')}
/>
</TouchableOpacity>
),
headerRight: (
<TouchableOpacity
onPress={() => navigation.navigate('Account')}>
<Image
style={{
width: 35,
height: 35,
marginRight: 25,
}}
source={require('../Images/user.png')}
/>
</TouchableOpacity>
)
})
},
Transaction: {
screen: Transaction,
navigationOptions: ({ navigate, navigation }) => ({
title:'Transaction',
headerTitleStyle: {
fontWeight: 'bold',
color: 'white',
marginLeft:5,
},
headerStyle: {
backgroundColor: '#FF6100',
},
headerLeft: (
<TouchableOpacity
onPress={() => navigation.navigate('Rechercher')}>
<Image
style={{
width: 35,
height: 35,
marginLeft: 25,
}}
source={require('../Images/logo.jpg')}
/>
</TouchableOpacity>
),
headerRight: (
<TouchableOpacity
onPress={() => navigation.navigate('Account')}>
<Image
style={{
width: 35,
height: 35,
marginRight: 25,
}}
source={require('../Images/user.png')}
/>
</TouchableOpacity>
)
})
},
})
export default createAppContainer(SearchStackNavigator)
Navigation.js is located on a folder named Navigation.
The two other files are on the same folder (The components one).
Can someone help me fixe it ? I don't think it's complicated, but there is something I didn't understand, I think.
Thank you in advance.
Noémie.
Your Register, and Login screens have to be listed as routes in your
stack-navigation ... so you can get access to navigation object to use
it for navigation – Hend El-Sahli
the program compiles perfect, but when I scan a product, it doesn't refresh in the view.
I have mounted a mysql xampp with all the products. when querying a barcode. I get in response the detail of the product, and, add the json response with parse object in a array.
the view refresh when click TouchableOpacity, later of scan a product.
import React, {Component, useState, useEffect} from 'react';
import {Dimensions,Image, View, Text,AsyncStorage, TouchableOpacity, Grid, StyleSheet, ScrollView,ToastAndroid} from 'react-native';
import { SearchBar, Card, Icon } from 'react-native-elements';
import { BarCodeScanner } from 'expo-barcode-scanner';
//import { NumberList } from './NumberList';
const {width, height} = Dimensions.get('window');
const urlApi = "http://192.168.1.24/rst/api/getProduct.php";
const numbers = [1,2,3,4,5,6];
class SaleScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
scan: false,
result: null,
ScanResult: false,
hasPermission: true,
setHasPermission: true,
productos: [],
jwt: null,
};
//this.componentDidMount();
}
async componentDidMount(){
let _jwt = await AsyncStorage.getItem('userToken');
this.setState({
jwt: _jwt
});
}
_searchProduct = async (data) => {
try {
let response = await fetch(urlApi,{
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer '+this.state.jwt
},
body: JSON.stringify({
CodiBarra: data
})
});
let responseJson = await response.json();
//console.log(responseJson);
if (responseJson.codibarra === null) {
//console.log("retorna falso");
return false;
}
else
{
if(this.state.productos != null)
{
this.state.productos.push(responseJson[0]);
//this.setState({productos: this.state.productos.push(responseJson[0])})
//console.log(this.state.productos);
}
else
{
this.state.productos.push(responseJson[0]);
}
}
} catch (error) {
console.error(error);
}
};
render(){
const { scan, ScanResult, result, hasPermission, setHasPermission, productos } = this.state;
const activeBarcode = ()=> {
this.setState({
scan: true
})
}
const handleBarCodeScanned = async ({ type, data }) => {
//console.log('scanned data' + data);
this.setState({result: data, scan: false, ScanResult: false});
this._searchProduct(data);
};
if (hasPermission === null) {
return <Text>Requesting for camera permission</Text>;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
return (
<View style= {{flex: 1}}>
{productos.length == 0 &&
<View style = {styles.button}>
<TouchableOpacity
onPress= {activeBarcode}
style={{
borderWidth:1,
borderColor:'rgba(0,0,0,0.2)',
alignItems:'center',
justifyContent:'center',
width:50,
height:50,
backgroundColor:'#fff',
borderRadius:50,
position: 'absolute',
marginTop: 10,
marginLeft: width - 100
}}
>
<Icon
type="font-awesome"
name="barcode"
/>
</TouchableOpacity>
</View>
}
{productos.length>0 &&
<View style={{flex:1}}>
<View style = {styles.button}>
<TouchableOpacity
onPress= {activeBarcode}
style={{
borderWidth:1,
borderColor:'rgba(0,0,0,0.2)',
alignItems:'center',
justifyContent:'center',
width:50,
height:50,
backgroundColor:'#fff',
borderRadius:50,
position: 'absolute',
marginTop: 10,
marginLeft: width - 100
}}
>
<Icon
type="font-awesome"
name="barcode"
/>
</TouchableOpacity>
</View>
<View style ={{flex:1,marginTop:20}}>
<ScrollView>
<NumberList products={this.state.productos} />
</ScrollView>
</View>
<View style ={{flex:1,marginTop:20}}>
<View style={{flexDirection:"row", height: 50, width: width, backgroundColor: "green", justifyContent: "center",}}>
<View style = {{ flexDirection:"column"}}>
<Text style = {{ fontSize: 40,color:"white"}}>{"TOTAL: "}</Text>
</View>
<View style={{justifyContent: "center", flexDirection:"row-reverse", paddingRight:20}}>
<Text style = {{ fontSize: 40,color:"white"}}>{result.precioventa}</Text>
</View>
</View>
</View>
</View>
}
{scan &&
<BarCodeScanner
onBarCodeScanned={handleBarCodeScanned}
style={StyleSheet.absoluteFillObject}
/>
}
</View>
);
}
}
function NumberList(props) {
console.log("NUMBERLIST")
console.log(props);
const products = props.products;
const listItems = products.map((product) =>
// Correct! Key should be specified inside the array.
<ListItem key={product.key} value={product} />
);
return (
<View>
{listItems}
</View>
);
}
function ListItem(props) {
// Correct! There is no need to specify the key here:
const u = props.value;
return (
<View>
{u &&
<Card>
<View style={{flexDirection:"row"}}>
<Icon
name = "monetization-on"
size = {40}
/>
<View style={{flex:1, flexDirection: "column", paddingTop: 10}}><Text>{u.descripcion}</Text></View>
<View style = {{flexDirection:"row-reverse", paddingTop: 10}}><Text>{u.precioventa}</Text></View>
</View>
</Card>
}
</View>);
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'black'
},
containerHome:{
backgroundColor: 'white',
height: height,
width: width,
flexDirection: 'column'
},
cardSales:{
backgroundColor: 'white',
flex: 1,
padding: 15,
borderWidth: 1,
borderRadius: 10,
borderColor: '#ddd',
borderBottomWidth: 7,
//shadowColor: '#000',
shadowColor: "#191919",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.8,
shadowRadius: 2,
elevation: 1,
marginLeft: 5,
marginRight: 5,
marginTop: 20,
},
cardContent:{
flexDirection: 'row-reverse',
fontSize: 50
},
button:{
flexDirection: 'column',
marginTop: 20,
marginLeft: 20,
marginRight: 20,
},
contentActionFast:{
backgroundColor: 'white',
flexDirection: 'row',
alignItems:'center',
justifyContent: 'space-between',
flex:1
}
});
export default SaleScreen;
if anyone could help me I'd really appreciate it
Your component isn't re-rendering because in _searchProduct you're doing this.state.productos.push(responseJson[0]); to update the state object when you should be using setState(). Directly modifying the state, which you're doing now, won't trigger a re-render and could lead to other problems, which is why you should never do it and always use setState() (or a hook) to update state. This is mentioned in the docs here: https://reactjs.org/docs/state-and-lifecycle.html#do-not-modify-state-directly.
Instead try:
this.setState((prevState) => ({
productos: [...prevState.productos, responseJson[0]]
});
I'm development some login screen with a simple Welcome screen,
but I have some issue with the state when refreshing the app it's still in the splash screen [Indicator] not separate,
I need when I'm removing the storage key update the state loading into another status but it doesn't work with me :L, can you help me about this issue?
myScreens
After first Login
When refreshing the app [double R key] - I need moving me to the Login form
this my Code
/Login.js
import React, { Component } from "react";
import {
StyleSheet,
View,
StatusBar,
Text,
TextInput,
TouchableOpacity,
ActivityIndicator,
AsyncStorage
} from "react-native";
export default class Login extends Component {
constructor() {
super();
this.state = {
username: "",
password: "",
isLoad: false
};
}
async componentWillMount() {
await AsyncStorage.getItem("#myApp:username").then(username => {
if (!username || username === "") {
this.setState({
isLoad: true
});
} else {
this.setState({ username: username });
this.props.navigation.navigate("Home");
}
});
}
_onLogin = () => {
let { username, password } = this.state;
if (username !== "" && password !== "") {
AsyncStorage.setItem("#myApp:username", this.state.username).then(
username => {
this.setState({ username: username });
}
);
this.props.navigation.navigate("Home");
}
};
render() {
if (!this.state.isLoad) {
return (
<View
style={{ flex: 1, justifyContent: "center", alignItems: "center" }}
>
<ActivityIndicator size="large" color="red" />
</View>
);
} else {
return (
<View style={styles.container}>
<StatusBar backgroundColor="#333" barStyle="light-content" />
<Text style={{ fontSize: 28, margin: 10 }}>Login</Text>
<TextInput
placeholder="Username"
onChangeText={username => {
this.setState({ username: username });
}}
value={this.state.username}
autoCorrect={false}
returnKeyType="next"
style={{
padding: 10,
margin: 15,
borderBottomColor: "#333",
borderBottomWidth: 1,
width: "80%"
}}
editable={true}
/>
<TextInput
placeholder="Password"
onChangeText={password => {
this.setState({ password: password });
}}
secureTextEntry={true}
value={this.state.password}
autoCorrect={false}
returnKeyType="next"
style={{
padding: 10,
margin: 15,
borderBottomColor: "#333",
borderBottomWidth: 1,
width: "80%"
}}
editable={true}
/>
<TouchableOpacity
style={{
margin: 20,
padding: 10,
width: "70%",
backgroundColor: "#1a73e8"
}}
onPress={this._onLogin}
>
<Text style={{ color: "#FFF", textAlign: "center", fontSize: 18 }}>
Log In
</Text>
</TouchableOpacity>
</View>
);
}
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#fff"
}
});
./Home.js
import React, { Component } from "react";
import {
StyleSheet,
View,
ImageBackground,
Alert,
Text,
AsyncStorage,
TouchableOpacity
} from "react-native";
export default class Home extends Component {
constructor() {
super();
this.state = {
name: ""
};
}
async componentDidMount() {
try {
const storedValue = await AsyncStorage.getItem("#myApp:username");
this.setState({ name: storedValue });
if (storedValue == null) {
this.props.navigation.navigate("Login");
}
} catch (error) {
Alert.alert("Error", "There was an error.");
}
}
_onLogout = () => {
AsyncStorage.removeItem("#myApp:username").then(() => {
this.setState({ name: "" });
});
this.props.navigation.navigate("Login");
};
render() {
return (
<View style={styles.container}>
<ImageBackground
source={require("./assets/bg1.jpg")}
style={{
flex: 1,
width: "100%",
height: "100%"
}}
>
<View
style={{
backgroundColor: "rgba(0,0,0,0)",
flex: 1,
justifyContent: "center"
}}
>
<Text
style={{
alignSelf: "center",
backgroundColor: "#000fff",
padding: 10,
color: "#fff"
}}
>
{`Hello, ${this.state.name} `}
</Text>
<TouchableOpacity
style={{
margin: 20,
padding: 10,
width: "50%",
backgroundColor: "#1a73e8",
justifyContent: "center",
alignSelf: "center"
}}
onPress={this._onLogout}
>
<Text
style={{ color: "#FFF", textAlign: "center", fontSize: 18 }}
>
Log out?
</Text>
</TouchableOpacity>
</View>
</ImageBackground>
</View>
);
}
}
The problem is in your componentWillMount
async componentWillMount() {
await AsyncStorage.getItem("#myApp:username").then(username => {
if (!username || username === "") {
this.setState({
isLoad: true // This is the issue
});
} else {
this.setState({ username: username });
this.props.navigation.navigate("Home");
}
});
}
First of all, you should avoid using componentWillMount and use componentDidMount as the former is now deprecated, and is not advised to use in the newer versions of react.
The problem is, when you navigate to the home screen after logging out, you are checking for username in the AsyncStorage but if its undefined, you're setting the isLoad state to true, which renders the ActivityIndicator. You should be doing isLoad before the promise is resolved if anything. If you replace your code with:
componentDidMount() {
this.setState({ isLoad: true });
AsyncStorage.getItem("#myApp:username").then(username => {
if (!username || username === "") {
this.setState({
isLoad: false // change this to False
});
} else {
this.setState({ username: username, isLoad: false });
this.props.navigation.navigate("Home");
}
});
}
You should be able to see a login Screen instead of the spinner. Hope this helps.
So I'm building a React Native app using
React Native - Latest
MobX and MobX-React - Latest
Firebase - Latest
My app works fine. However, I can leave the app idle or play around with it and after 30-90 seconds I red screen with this error. Its not being very specific about what file is erroring! How can I debug this?
Firebase.js
export function getFeed(db,id,callback){
db.collection("posts").where("userId", "==", id)
.get()
.then(function (querySnapshot) {
callback(true,querySnapshot)
})
.catch(function (error) {
callback(false)
console.log("Error getting documents: ", error);
});
}
List.js
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
View,
TouchableOpacity,
FlatList,
ActivityIndicator,
RefreshControl
} from 'react-native';
import Post from './Post';
import Spinner from 'react-native-spinkit';
import { Icon } from 'react-native-elements';
import { getFeed } from '../../network/Firebase';
import { observer, inject } from 'mobx-react';
#inject('mainStore')
#observer export default class List extends Component {
constructor(props) {
super(props)
this.state = {
dataSource: [],
initialLoad: true,
refreshing: false
}
this.getData = this.getData.bind(this)
}
componentWillMount() {
this.getData()
}
getData() {
this.setState({
refreshing: true
})
getFeed(this.props.screenProps.db, this.props.mainStore.userData.id, (status, res) => {
let tempArray = []
let counter = 0
res.forEach((doc) => {
let tempObj = doc.data()
doc.data().user
.get()
.then((querySnapshot) => {
tempObj.userData = querySnapshot.data()
tempArray.push(tempObj)
counter = counter + 1
if (counter === res.docs.length - 1) {
this.setState({
dataSource: tempArray,
initialLoad: false,
refreshing: false
})
}
})
});
})
}
renderRow = ({ item }) => {
return (
<Post item={item} />
)
}
render() {
if (this.state.initialLoad) {
return (
<View style={styles.spinner}>
<Spinner isVisible={true} type="9CubeGrid" size={40} color="white" />
</View>
)
} else {
return (
<FlatList
data={this.state.dataSource}
extraData={this.state}
keyExtractor={(_, i) => i}
renderItem={(item) => this.renderRow(item)}
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this.getData}
/>
}
/>
);
}
}
}
const styles = StyleSheet.create({
spinner: {
marginTop: 30,
alignItems: 'center'
}
});
Post.js
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
View,
TouchableOpacity,
Image
} from 'react-native';
import moment from 'moment';
import { Icon } from 'react-native-elements';
export default class Post extends React.PureComponent {
render() {
let today = moment()
let date = this.props.item.date
if(today.diff(date, 'days') < 5){
date = moment(date).startOf('day').fromNow()
}else{
date = moment(date).format('DD MMM YYYY, h:mm a')
}
return (
<View
style={styles.container}
>
<View style={styles.top}>
<Image style={styles.profile} source={{uri: this.props.item.userData.image}} />
<Text style={styles.title}>{this.props.item.userData.firstName+' '+this.props.item.userData.lastName}</Text>
</View>
<View style={styles.descriptionContainer}>
<Text style={styles.description}>{this.props.item.description}</Text>
</View>
<View style={styles.imageContainer}>
<Image style={styles.image} source={{uri: this.props.item.image}} />
</View>
<TouchableOpacity style={styles.commentsContainer}>
<View style={styles.timeFlex}>
<Text style={styles.title}>{date}</Text>
</View>
<View style={styles.commentFlex}>
<Text style={styles.title}>Comments (12)</Text>
</View>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
title: {
color: 'white',
backgroundColor: 'transparent'
},
timeFlex: {
flex: 0.5,
alignItems: 'flex-start'
},
commentFlex: {
flex: 0.5,
alignItems: 'flex-end'
},
profile: {
width: 40,
height: 40,
borderRadius: 20,
marginRight: 10
},
descriptionContainer: {
marginBottom: 10,
marginHorizontal: 15,
},
description: {
color: 'rgba(255,255,255,0.5)'
},
commentsContainer: {
marginBottom: 10,
alignItems: 'flex-end',
marginHorizontal: 15,
flexDirection: 'row'
},
imageContainer: {
marginBottom: 10,
marginHorizontal: 15,
height: 180
},
image: {
height: '100%',
width: '100%'
},
top: {
justifyContent: 'flex-start',
margin: 10,
marginLeft: 15,
flexDirection: 'row',
alignItems: 'center'
},
container: {
margin: 10,
backgroundColor: '#243c5e',
borderRadius: 10,
shadowColor: 'black',
shadowOffset: {
width: 2,
height: 1
},
shadowRadius: 4,
shadowOpacity: 0.3
}
});