export default class HistoryScreen extends BaseScreen {
constructor(props){
super(props);
this.state = {
mainListData:[],
listData:[],
searchText:'',
};
}
listRowPressed(item) {
this.props.navigation.navigate('Details', {
checkin: item.data
});
}
render() {
return (
<View style={styles.container}>
<View style={{ flex:1, backgroundColor: '#FFF'}}>
<FlatList
data={this.state.listData}
renderItem={({item}) => <ListComp data={item} />}
keyExtractor={(item, index) => index.toString()}
/>
</View>
</View>
);
}
}
const ListComp = item => (
<TouchableOpacity onPress={() => this.listRowPressed(item)
}>
<View style={styles.row}>
</View>
</TouchableOpacity>
);
I am displaying data in FlatList, however clicking on item gives me this4. listRowPressed is undefined, I tried binding the function too but didn't work. What is wrong in the code?
You have to pass listRowPressed in your ListComp component. Your whole code should be like this :
export default class HistoryScreen extends BaseScreen {
constructor(props) {
super(props);
this.state = {
mainListData: [],
listData: [],
searchText: '',
};
}
listRowPressed = (item) => {
this.props.navigation.navigate('Details', {
checkin: item.data
});
}
render() {
return (
<View style={styles.container}>
<View style={{ flex: 1, backgroundColor: '#FFF' }}>
<FlatList
data={this.state.listData}
renderItem={({ item }) => <ListComp data={item} listRowPressed={this.listRowPressed} />}
keyExtractor={(item, index) => index.toString()}
/>
</View>
</View>
);
}
}
const ListComp = (props) => (
<TouchableOpacity
onPress={() => props.listRowPressed(props.item)}
>
<View style={styles.row}>
{/* Do whatever with props.item here */}
</View>
</TouchableOpacity>
);
Also note that I have converted your method listRowPressed simple function to arrow function.
Try this
<FlatList
data={this.state.listData}
renderItem={this.listComp}
keyExtractor={(item, index) => index.toString()}
/>
listComp = ({item}) => (
return(
<TouchableOpacity onPress={() => this.listRowPressed(item)} >
<View style={styles.row}>
</View>
</TouchableOpacity>
);
);
Related
I'm using DropDownPicker in react native but onChangeValue event is not triggering.
I have used onChange and onChangeItem already. Trying to follow this https://hossein-zare.github.io/react-native-dropdown-picker-website/docs/usage#onchangevalue but its not working. Please help me out.
Below is the code:
import React, { useEffect, useState } from 'react';
import {View, Text, Button, TextInput, FlatList, ActivityIndicator, StyleSheet, Image} from 'react-native';
import filter from 'lodash.filter';
import DropDownPicker from 'react-native-dropdown-picker';
const CarList = () => {
const [isLoading, setIsLoading] = useState(false);
const [data, setData] = useState([]);
const [error, setError] = useState(null);
const [query, setQuery] = useState('');
const [fullData, setFullData] = useState([]);
const [selected, setSelected] = useState("");
const [open, setOpen] = useState(false);
const [childOpen, setChildOpen] = useState(false);
const [filterOption, setfilteroption] = useState([
{label: 'Color', value: 'Color'},
{label: 'Model', value: 'Model'},
{label: 'Year', value: 'Year'}
]);
const [value, setValue] = useState(null);
const [childvalue, setChildValue] = useState([]);
useEffect(() => {
setIsLoading(true);
fetch(`https://myfakeapi.com/api/cars/?seed=1&page=1&results=20`)
.then(response => response.json())
.then(response => {
setData(response.cars);
setFullData(response.cars);
setIsLoading(false);
})
.catch(err => {
setIsLoading(false);
setError(err);
});
}, []);
if (isLoading) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<ActivityIndicator size="large" color="#5500dc" />
</View>
);
}
if (error) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ fontSize: 18}}>
Error fetching data... Check your network connection!
</Text>
</View>
);
}
const handleSearch = text => {
const formattedQuery = text.toLowerCase();
const filteredData = filter(fullData, user => {
return contains(user, formattedQuery);
});
setData(filteredData);
setQuery(text);
};
const contains = ({ car, car_model,car_color }, query) => {
if (car.toLowerCase().includes(query) || car_model.toLowerCase().includes(query) || car_color.toLowerCase().includes(query)) {
return true;
}
return false;
};
const color = [...new Set(data.map((item) => item.car_color))];
const model = [...new Set(data.map((item) => item.car_model))];
const year = [...new Set(data.map((item) => item.car_model_year))];
//this is not triggering
const changeSelectOptionHandler = (value) => {
console.log("hi")
setSelected(value);
};
if (selected === "Color") {
setChildValue(color)
} else if (selected === "Model") {
setChildValue(model)
} else if (selected === "Year") {
setChildValue(year)
}
function renderHeader() {
return (
<View
style={{
backgroundColor: '#fff',
padding: 10,
marginVertical: 10,
borderRadius: 20
}}
>
<TextInput
autoCapitalize="none"
autoCorrect={false}
clearButtonMode="always"
value={query}
onChangeText={queryText => handleSearch(queryText)}
placeholder="Search"
style={{ backgroundColor: '#fff', paddingHorizontal: 20 }}
/>
<DropDownPicker onChangeItem={changeSelectOptionHandler}
open={open}
value={value}
items={filterOption}
setOpen={setOpen}
setValue={setValue}
setItems={setfilteroption}
dropDownDirection="TOP"
style={{
padding: 5,
margin: 5,
width: 200,
flexDirection: 'row'
// borderRadius: 20
}}
/>
<DropDownPicker
open={childOpen}
items={childvalue}
setOpen={setChildOpen}
setItems={setChildValue}
dropDownDirection="TOP"
/>
</View>
);
}
return (
<View style={styles.container}>
<Text style={styles.text}>Favorite Contacts</Text>
<FlatList
keyboardShouldPersistTaps="always"
ListHeaderComponent={renderHeader}
data={data}
keyExtractor={({ id }) => id}
renderItem={({ item }) => (
<View style={styles.listItem}>
<Image
source={{
uri: 'https://picsum.photos/200',
}}
style={styles.coverImage}
/>
<View style={styles.metaInfo}>
<Text style={styles.title}>{`${item.car} ${
item.car_model
}`}</Text>
<Text>Color: {`${item.car_color}`}</Text>
<Text>Price: {`${item.price}`}</Text>
</View>
</View>
)}
/>
</View>
);
}
I needed to use onSelectItem instead of onChangeValue
The latest version of dropdownpicker
OnChangeValue is not supported.
use this :
onSelectItem={(item) => {console.log(item.value)}}
Note: I am using "react-native-dropdown-picker": "^5.4.2",
What I want to achieve is to redirect or show a view upon successful purchase. Purchase is successful but the state variable is not updating inside validateReceiptIos handler.
I checked the validation of the receipt but after that not even able to redirect to another page or load a view on the condition. I am attaching a code snippet below.
const productIds = ['xxx_xx_xx'];
const InAppPurchase = ({ navigation }) => {
const [products, setProducts] = useState([]);
const [purchased, setPurchased] = useState(false);
const [email, setEmail] = useState({
emailText: '',
emailValidity: false,
emailErrorText: null,
});
const [name, setName] = useState({
nameText: '',
nameValidity: false,
nameErrorText: null,
});
const validateUserName = (text) => {
if (text.length < 1) {
setName({ nameText: text, nameValidity: false, nameErrorText: 'Name cannot be empty' });
}
else { setName({ nameText: text, nameValidity: true, nameErrorText: '' }); }
};
const validateEmail = (text) => {
let reg = /^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/;
if (reg.test(text) === false && text.length > 0) {
setEmail({ emailText: text, emailValidity: false, emailErrorText: 'Invalid email' });
} else if (text.length < 1) {
setEmail({ emailText: text, emailValidity: false, emailErrorText: 'Email cannot be empty' });
}
else { setEmail({ emailText: text, emailValidity: true, emailErrorText: '' }); }
};
const validate = async (receipt, purchase) => {
const receiptBody = {
"receipt-data": receipt,
"password": "xxxxxxxxxxxxxxxxxxxxxx"
}
const result = await IAP.validateReceiptIos(receiptBody, true).catch(() => {
}).then((receipt) => {
try {
const renewalHistory = receipt.latest_receipt_info;
const expiration = renewalHistory[renewalHistory.length - 1].expires_date_ms;
// const expiration = renewalHistory[0].expires_date_ms;
let expired = Date.now() > expiration;
if (!expired) {
IAP.finishTransaction(purchase);
setPurchased(true); //***Not updating the state variable***
//navigation.navigate('Success') //***Not even navigate to another page***
} else {
Alert.alert("Purchase expired", "Your subscription has been expired, please resubscribe to continue using the app")
}
} catch (error) {
console.log(error)
}
})
}
useEffect(() => {
IAP.getProducts(productIds).then(res => {
setProducts(res);
})
const purchaseUpdateSubscription = IAP.purchaseUpdatedListener(purchase => {
const receipt = purchase.transactionReceipt;
if (receipt) {
validate(receipt, purchase)
}
})
return () => {
purchaseUpdateSubscription.remove();
}
}, []);
if (purchased) {
return (
<SafeAreaView style={styles.container}>
<View style={styles.containerInner}>
<View style={{ flex: 1, flexDirection: "column" }}>
<View style={styles.logoSuccessSection}>
<Image style={styles.successLogo} source={require('../images/success.png')} />
</View>
<View style={{ flex: 1 }}>
<TouchableOpacity style={styles.button} onPress={() => navigation.navigate('Login')}>
<Text style={styles.signText}>CONTINUE</Text>
</TouchableOpacity>
</View>
</View>
</View>
</SafeAreaView>
)
} else {
return (
<SafeAreaView style={styles.container}>
<View style={styles.containerInner}>
<View style={styles.logoSection}>
<Image style={styles.logo} source={require('../images/logo.jpg')} />
</View>
<View style={styles.headerContainer}>
<Text style={styles.headerText}>PREMIUM ACCESS</Text>
</View>
<View style={styles.featureContainer}>
<ScrollView style={{ flexDirection: 'column', paddingVertical: 20 }}>
<TextInput blurOnSubmit={true} autoCapitalize='none' placeholderTextColor="#736c6c"
style={{ height: 40, borderColor: '#d6d8e1', borderWidth: 1, paddingHorizontal: 10 }} placeholder="Email" onChangeText={(text) => validateEmail(text)} onSubmitEditing={(event) => setEmail({ emailText: event.nativeEvent.text })}
/>
{email.emailValidity === false ? <Text style={styles.errorTextStyle}>{email.emailErrorText}</Text> : null}
<TextInput blurOnSubmit={true} autoCapitalize='none' placeholderTextColor="#736c6c"
style={{ height: 40, borderColor: '#d6d8e1', borderWidth: 1, paddingHorizontal: 10, marginVertical: 5 }} placeholder="Name" onChangeText={(text) => validateUserName(text)} onSubmitEditing={(event) => setName({ nameText: event.nativeEvent.text })}
/>
{name.nameValidity === false ? <Text style={styles.errorTextStyle}>{name.nameErrorText}</Text> : null}
</ScrollView>
</View>
<View>
{products.map((product, i) =>
<TouchableOpacity key={i} style={styles.button} onPress={() => {
if (email.emailValidity === false || name.nameValidity === false) {
Alert.alert(
"Email & Name fields cannot be empty.",
"",
[
{ text: "OK", onPress: () => console.log("OK Pressed") }
],
{ cancelable: false }
);
} else {
IAP.requestSubscription(product.productId);
}
}}>
<Text style={styles.signText}>SUBSCRIBE - $9.99/Month</Text>
</TouchableOpacity>
)}
</View>
</View>
</SafeAreaView>
)
}
}
I have been battling with the display of database item on my page using this.state.noteArray.map(val, key). I intend to display each value with a delete button to remove it from the page.
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
TextInput,
ScrollView,
TouchableOpacity
} from 'react-native';
import firebase from 'firebase';
// Initialize Firebase
const config = {
apiKey: "XXXXXXXXXXXXXXX",
authDomain: "XXXXXXXXXXXXXXXXXXXXXX",
databaseURL: "XXXXXXXXXXXXXXXXXXXXXXXX",
projectId: "XXXXXXXXXXXXXXXXXXXXXX",
storageBucket: "",
messagingSenderId: "XXXXXXXXXXXXXXXXXXXX"
};
firebase.initializeApp(config);
export default class Main extends Component {
constructor(props){
super(props);
this.state = {
noteArray: [],
noteText: '',
};
this.addNote = this.addNote.bind(this);
}
componentDidMount(){
firebase.database()
.ref()
.child("todo")
.once("value", snapshot => {
const data = snapshot.val()
if (snapshot.val()){
const initNoteArray = [];
Object
.keys(data)
.forEach(noteText => initNoteArray.push(data[noteText]));
this.setState({
noteArray: initNoteArray
});
}
});
firebase.database()
.ref()
.child("todo")
.on("child_added", snapshot => {
const data = snapshot.val();
if (data){
this.setState(prevState => ({
noteArray: [data, ...prevState.noteArray]
}))
console.log(this.state.noteArray);
}
})
}
addNote(){
// firebase function here to send to the database
if (!this.state.noteText) return;
var d = new Date();
const newNote = firebase.database().ref()
.child("todo")
.push ({
'date':d.getFullYear()+
"/"+(d.getMonth()+1) +
"/"+ d.getDate(),
'note': this.state.noteText
});
newNote.set(this.state.noteText, () => this.setState({noteText: ''}))
}
render() {
let notes = this.state.noteArray.map((val, key)=>{
return
(<View key={key} keyval={key} val={val} style={styles.note}>
<Text style={styles.noteText}>{this.state.val.date}</Text>
<Text style={styles.noteText}>{this.state.val.note}</Text>
<TouchableOpacity onPress={this.state.deleteMethod} style={styles.noteDelete}>
<Text deleteMethod={()=>this.deleteNote(key)} style={styles.noteDeleteText}>D</Text>
</TouchableOpacity>
</View>)
});
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerText}>Todo App</Text>
</View>
<ScrollView style={styles.scrollContainer}>
{notes}
</ScrollView>
<View style={styles.footer}>
<TextInput
style={styles.textInput}
placeholder='>note'
onChangeText={(noteText)=> this.setState({noteText})}
value={this.state.noteText}
placeholderTextColor='white'
underlineColorAndroid='transparent'>
</TextInput>
</View>
<TouchableOpacity onPress={ this.addNote } style={styles.addButton}>
<Text style={styles.addButtonText}>+</Text>
</TouchableOpacity>
</View>
);
}
deleteNote(key){
this.state.noteArray.splice(key, 1);
this.setState({noteArray: this.state.noteArray});
}
}
There is no warning or error, but it is not displaying anything. I will appreciate if there is any help and inline comment to understand the process for the next time, I am a newbie, trying to master the code for future similar projects. All I care to know is the basic understanding of the CRUD and search using React native firebase. Thank you so much
If I correctly understand you need a proper way to display your data. Since noteArray is an array, there's nothing easier than a FlatList, which is scrollable by itself.
So, in your render method:
render() {
return (
<View style={styles.container}>
<FlatList
data={this.state.noteArray} // Here is where you pass your array of data
renderItem={this.renderItem} // Here is how to display each item of your data array
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderFooter}
/>
</View>
);
}
Where:
renderHeader = () => {
return (
<View style={styles.header}>
<Text style={styles.headerText}>Todo App</Text>
</View>
)
}
renderFooter = () => {
return (
<View>
<View style={styles.footer}>
<TextInput
style={styles.textInput}
placeholder='>note'
onChangeText={(noteText)=> this.setState({noteText})}
value={this.state.noteText}
placeholderTextColor='white'
underlineColorAndroid='transparent'>
</TextInput>
</View>
<TouchableOpacity onPress={ this.addNote } style={styles.addButton}>
<Text style={styles.addButtonText}>+</Text>
</TouchableOpacity>
</View>
)
}
renderItem = ({ item, index }) => {
return (
<View key={index} style={styles.note}>
<Text style={styles.noteText}>{item.date}</Text>
<Text style={styles.noteText}>{item.note}</Text>
<TouchableOpacity onPress={this.state.deleteMethod} style={styles.noteDelete}>
<Text deleteMethod={()=>this.deleteNote(index)} style={styles.noteDeleteText}>D</Text>
</TouchableOpacity>
</View>
)
}
Thanks for your support. I have reviewed my code and it is working perfectly as I want. I will love to post it here in case someone else needs to work or learn about it. I used array.map() function to iterate over the items.
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
TextInput,
ScrollView,
TouchableOpacity
} from 'react-native';
import Note from './Note';
import firebase from 'firebase';
// Initialize Firebase
const config = {
apiKey: "XXXXXXXXXXXXXXXXXXXXXXXXXX",
authDomain: "XXXXXXXXXXXXXXXXXXXXXX",
databaseURL: "XXXXXXXXXXXXXXXXXXXXXXXX",
projectId: "XXXXXXXXXXXXXXXXXXXXXXXXX",
storageBucket: "",
messagingSenderId: "XXXXXXXXXXXXXXXX"
};
firebase.initializeApp(config);
export default class Main extends Component {
constructor(props){
super(props);
this.state = {
noteArray: [],
noteText: '',
};
this.addNote = this.addNote.bind(this);
}
componentDidMount(){
firebase.database()
.ref()
.child("todo")
.once("value", snapshot => {
const data = snapshot.val()
if (snapshot.val()){
const initNoteArray = [];
Object
.keys(data)
.forEach(noteText => initNoteArray.push(data[noteText]));
this.setState({
noteArray: initNoteArray
});
}
});
firebase.database()
.ref()
.child("todo")
.on("child_added", snapshot => {
const data = snapshot.val();
if (data){
this.setState(prevState => ({
noteArray: [data, ...prevState.noteArray]
}))
console.log(this.state.noteArray);
}
})
}
addNote(){
// firebase function here to send to the database
if (!this.state.noteText) return;
var d = new Date();
const newNote = firebase.database().ref()
.child("todo")
.push ();
newNote.set({
'date':d.getFullYear()+
"/"+(d.getMonth()+1) +
"/"+ d.getDate(),
'note': this.state.noteText
});
this.setState({noteText:''});
}
render() {
let notes = this.state.noteArray.map((val, key)=>{
return <Note key={key} keyval={key} val={val}
deleteMethod={()=>this.deleteNote(key)}/>
});
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerText}>Todo App</Text>
</View>
<ScrollView style={styles.scrollContainer}>
{notes}
</ScrollView>
<View style={styles.footer}>
<TextInput
style={styles.textInput}
placeholder='>note'
onChangeText={(noteText)=> this.setState({noteText})}
value={this.state.noteText}
placeholderTextColor='white'
underlineColorAndroid='transparent'>
</TextInput>
</View>
<TouchableOpacity onPress={ this.addNote } style={styles.addButton}>
<Text style={styles.addButtonText}>+</Text>
</TouchableOpacity>
</View>
);
}
deleteNote(key){
this.state.noteArray.splice(key, 1);
this.setState({noteArray: this.state.noteArray});
}
}
I have another .js component named Note.js for display template. This was included in the Main.js and was reference just after the render.
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
} from 'react-native';
export default class Note extends Component {
render() {
return (
<View key={this.props.keyval} style={styles.note}>
<Text style={styles.noteText}>{this.props.val.date}</Text>
<Text style={styles.noteText}>{this.props.val.note}</Text>
<TouchableOpacity onPress={this.props.deleteMethod} style={styles.noteDelete}>
<Text style={styles.noteDeleteText}>D</Text>
</TouchableOpacity>
</View>
);
}
}
I want to Push to a new Component by the function push() in NavigatorIOS. It's like following:
renderRow(rowData, sectionID, rowID) {
var imgSource = IMAGE_URLS[rowID];
return (
<TouchableHighlight onPress = {() => {
this.props.navigator.push({
title: 'test',
component: example,
});
}}>
<View>
<View style={styles.row}>
<Image
source={imgSource}
style={styles.thum}
/>
<Text style={styles.text}>
{rowData}
</Text>
</View>
</View>
</TouchableHighlight>
);
}
But it will get a error when I click the TouchableHighlight.
I refered these two questions(1 and 2) before this. And the complete code is in this link
this is not binded to the class inside of renderRow().
You have to bind this either in the constructor:
this.renderRow = this.renderRow.bind(this);
or inside the render method:
render() {
var navStatusBarConfig = {
style: 'light-content',
}
return (
<View style={{ flex: 1, backgroundColor: '#F5FCFF'}}>
<View styles={styles.nav}></View>
<ListView
automaticallyAdjustContentInsets={false}
contentContainerStyle={styles.list}
dataSource={this.state.dataSource}
pageSize={4}
renderRow={this.renderRow.bind(this)}
/>
</View>
);
}
}
As to why, here is the reason :
https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding
And a more complete blog about how to bind this [there are many, blogs and ways to bind this]) :
http://blog.andrewray.me/react-es6-autobinding-and-createclass/
I'm trying to upload images that I've base64 encoded but it doesn't seem to save the state to the app so I can upload the images to my server. The state only seems to save within the function and not to the entire component. Any suggestions as to what I'm doing wrong?
_selectImage(uri) {
NativeModules.ReadImageData.readImage(uri, (image) => {
this.state = {
selected: image
};
console.log(this.state)
});
}
render() {
return (
<View style={styles.container}>
<View style={{ flex: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', }}>
<TouchableOpacity style={ styles.button } onPress={ this._addImage.bind( this ) }>
<Text>Add Image</Text>
</TouchableOpacity>
<TouchableOpacity style={ styles.button } onPress={this._uploadImage} >
<Text>Upload Image</Text>
</TouchableOpacity>
</View>
<ScrollView style={styles.container}>
<View style={styles.imageGrid}>
{ this.state.images.map((image) => {
return (
<TouchableHighlight onPress={this._selectImage.bind(null, image.uri)}>
<Image key={ _generateUUID() } style={styles.image} source={{ uri: image.uri }} />
</TouchableHighlight>
);
})
}
</View>
</ScrollView>
</View>
);
}
};
First, this-bind your _selectImage-method in constructor. Then use "setState" instead of this.state = {...}
You need only once to create the state object, best in constructor. Use then setState instead for further modifications.
constructor(props) {
super(props);
this.state = {selected: null};
this._selectImage = this._selectImage.bind(this);
}
_selectImage(uri) {
NativeModules.ReadImageData.readImage(uri, (image) => {
this.setState({
selected: image
});
console.log(this.state)
});
}