I'm new to React Native and Javascript, but I cannot find anything online to help me with this problem that I am having.
My "macSong" functions if statements never pass and I'm unsure as to why, I feel like the logic behind the code is sound, yet my console is still outputting "Selected Item is Unknown" because all of the if else statements are "not true" when at least one of them should be true if the dropdown menu has been used before pressing the button. My macSong function is just above my Stylesheet, at the bottom of my code.
If anyone can help me that would be amazing, thank you in advance and be sure to let me know if you need any more information to help you answer my question!
import React, { Component, Fragment } from 'react';
import { StyleSheet, Text, TextInput, View, Button, Alert } from 'react-native';
import SearchableDropdown from 'react-native-searchable-dropdown';
var items =[
{
id: 1,
name: 'Happy Music'
},
{
id: 2,
name: 'Sad Music'
},
{
id: 3,
name: 'Chill Music'
},
{
id: 4,
name: 'Hype Music'
}
];
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
selectedItems: ''
}
}
render() {
return (
<View style={ styles.screen }>
<Fragment>
{/* Title */}
<View style={ styles.title }>
<Text> Which Mac Miller Song Matches Your Mood? </Text>
</View>
{/* Single Dropdown Menu */}
<SearchableDropdown
onItemSelect={(item) => {
const items = this.state.selectedItems;
this.setState({ selectedItems: [...items, item]});
}}
containerStyle={{ padding: 25, alignSelf: 'center' }}
onRemoveItem={(item, index) => {
const items = this.state.selectedItems.filter((sitem) => sitem.id !== item.id);
this.setState({ selectedItems: [...items, item] });
}}
itemStyle={{
padding: 10,
marginTop: 2,
backgroundColor: '#ddd',
borderColor: '#bbb',
borderWidth: 1,
borderRadius: 5,
}}
itemTextStyle={{ color: '#222' }}
itemsContainerStyle={{ maxHeight: 140 }}
items={items}
defaultIndex={''}
resetValue={false}
textInputProps={
{
placeholder: "What kind of music do you want to hear?",
underlineColorAndroid: "transparent",
style: {
padding: 12,
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 5,
},
}
}
listProps={
{
nestedScrollEnabled: true,
}
}
/>
{/* Button */}
<View style={ styles.button }>
<Button
title="Press me for a Mac Miller song!"
onPress={() =>
this.macSong()}
/>
</View>
</Fragment>
</View>
);
}
/* Different Mood Function */
macSong() {
console.log(this.state.selectedItems)
if (this.state.selectedItems.some(item => item.name === 'Happy Music')) {
let songs = ['best day ever', 'kool aid & frozen pizza', 'nikes on my feet'];
let song = songs[Math.floor(Math.random() * songs.length)];
console.log(song);
} else if (this.state.selectedItems.some(item => item.name === 'Sad Music')) {
let songs = ['self care', 'ROS', 'stay', 'whats the use'];
let song = songs[Math.floor(Math.random() * songs.length)];
console.log(song);
} else if (this.state.selectedItems.some(item => item.name === 'Chill Music')) {
let songs = ['good news', 'claymation', 'the star room'];
let song = songs[Math.floor(Math.random() * songs.length)];
console.log(song);
} else if (this.state.selectedItems.some(item => item.name === 'Hype Music')) {
let songs = ['donald trump', 'remember', 'weekend'];
let song = songs[Math.floor(Math.random() * songs.length)];
console.log(song);
} else {
console.log("Selected Item is Unknown");
}
}
}
{/* StyleSheet */}
const styles = StyleSheet.create({
button: {
padding: 10,
alignSelf: 'center'
},
title: {
padding: 30,
alignSelf: 'center',
textAlign: 'center'
}
});
this.state.selectedItems is an array of objects of the following form:
{ id: Number, name: String }
If we look at your if statements, your conditionals look like:
if (this.state.selectedItems.includes('Happy Music')) {
Your array of objects will never include a plain string. You want to check if in includes an object with the name of that String. You can use some for that. Something like this:
if (this.state.selectedItems.some(item => item.name === 'Happy Music'))
Hopefully that gets you back to a working state. Peeking at your code it looks like you have opportunity to simplify this logic after this.
First, you are calling macSong() with no arguments on button press, but on its definition you have an argument macSong(selectedArguments) { ... }. This is not needed because you are grabbing this.state.selectedItems within your function. So the correct would be macSong() { ... }.
Then, it should be this.state.selectedItems.map(item => item.name).includes("Happy Music") etc., because item is an object that has name as a property, and when you check if an object includes("string") you need to check an array of strings (which map() will do).
Related
in my Tinder like react-native app I have a huge list of tags that people can add to their profile when they click on a tag, I want the tags the user adds to be a different color however, the background color is not changing... only time everything looks as it should is when I change the view/code and the component refreshes, then the colored tags appear.
This is my code:
const [selectedItems, setSelectedItems] = useState([]);
const addItem = (id) =>
{
console.log(id);
if(selectedItems.includes(id))
{
let index = selectedItems.findIndex(interest => interest == id);
if(index > -1)
{
let selectedInterests = selectedItems;
selectedInterests.splice(index, 1)
setSelectedItems(selectedInterests);
}
}
else
{
if(selectedItems.length < 10)
{
let selectedInterests = selectedItems;
selectedInterests.push(id);
setSelectedItems(selectedInterests);
}
}
};
{root.userStore.tags.map((item,index) => {return (
<Text key={item.id} onPress={ ()=>{ addItem(item.id) } } style={{ fontSize:17,padding:6,paddingLeft:10,paddingRight:10, ...selectedItems.includes(item.id) ? { color:'white', borderColor:'#E13545',backgroundColor:'#E13545' } : { color:'rgb(100,100,100)',borderColor:'rgba(0,0,0,0.1)',backgroundColor:'white' },borderRadius:35,borderWidth:1,margin:5 }}>{I18n.t(item.title)}</Text>
)
})}
Thanks in advance
Something does not appear to be correct with your destructuring, to use multiple styles you can try using a styles array, I did not get a chance to run this but should work. You can check documentation of the style array here, you may also consider using a flatlist for this if possible
{
root.userStore.tags.map((item, index) => {
return (
<Text
key={item.id}
onPress={() => {
addItem(item.id);
}}
style={[
{
fontSize: 17,
padding: 6,
paddingLeft: 10,
paddingRight: 10,
borderRadius: 35,
borderWidth: 1,
margin: 5,
},
selectedItems.includes(item.id)
? {
color: "white",
borderColor: "#E13545",
backgroundColor: "#E13545",
}
: {
color: "rgb(100,100,100)",
borderColor: "rgba(0,0,0,0.1)",
backgroundColor: "white",
},
]}
>
{I18n.t(item.title)}
</Text>
);
});
I am having 2 problems using React Native and Firebase Real Time Database.
When I add something to the list with the text input, all the list itens are duplicated except the item that I just added, this problem is only solved when I refresh the app screen.
When I remove something from firebase dashboard or other client, the list is not updated real time.
import React, {useState, Component} from 'react';
import {
Text,
View,
Switch,
StyleSheet,
FlatList,
TextInput,
Button,
TouchableOpacity,
SafeAreaView,
VirtualizedList,
} from 'react-native';
import database from '#react-native-firebase/database';
class MenuBreakFastScreen extends React.Component {
state = {newItem: ''};
state = {itens: []};
componentDidMount() {
let dbRef = database().ref('/cafe/itens/');
this.listenerFirebase(dbRef);
}
listenerFirebase(dbRef) {
dbRef.on('value', dataSnapshot => {
const newItens = JSON.parse(JSON.stringify(this.state.itens));
dataSnapshot.forEach(child => {
newItens.push({
name: child.val().name,
key: child.key,
});
this.setState({itens:newItens});
});
});
}
addItem() {
if (this.state.newItem === '') {
return;
}
database().ref('/cafe/itens/').push({
name: this.state.newItem,
});
this.setState({
newItem: '',
});
}
render() {
const {itens} = this.state;
const {newItem} = this.state;
const renderItem = ( {item}) => {
return(
<ItemAsset title={item.name}/>
);
}
return (
<SafeAreaView
style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<FlatList
data={itens}
renderItem={renderItem}
keyExtractor={item => item.key}
/>
<SafeAreaView style={{flexDirection: 'row'}}>
<TextInput
style={styles.input}
onChangeText={text =>
this.setState({
newItem: text,
})
}
value={newItem}
/>
<TouchableOpacity style={styles.Botao} onPress={() => this.addItem()}>
<Text style={styles.BotaoTexto}>+</Text>
</TouchableOpacity>
</SafeAreaView>
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
texto: {
fontSize: 35,
},
input: {
color: '#000',
fontSize: 22,
borderWidth: 1,
flex: 8,
margin: 10,
},
BotaoTexto: {
color: '#fff',
fontSize: 22,
},
Botao: {
backgroundColor: '#000',
marginTop: 10,
padding: 10,
flex: 1,
alignItems: 'center',
margin: 10,
},
ListaContainer: {
flexDirection: 'row',
backgroundColor: '#000',
flex: 1,
},
item: {
backgroundColor: '#000',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
flexDirection: 'row',
},
title: {
color: '#ffff',
fontSize: 32,
},
});
const ItemAsset = ( {title} ) => {
return(
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
}
export default MenuBreakFastScreen;
When you are listen for real time changes on real-time database it will send all the items with snapshot when any data is changed. That happens because you are listen for whole list, not only for a single item. Therefore you do not need to get the current list from state. You just have to set the state with retrieved data.
listenerFirebase(dbRef) {
dbRef.on('value', dataSnapshot => {
const newItens = []; // This should be initially empty array. That's all.
dataSnapshot.forEach(child => {
newItens.push({
name: child.val().name,
key: child.key,
});
});
this.setState({itens:newItens});
});
}
After correcting this part the error you got when removing data will be also resolved.
I have this code where I am trying to render an element:
render() {
//console.log('this.state.showRespondTo:',this.state.showRespondTo);
return (
<View style={{flex:1}}>
{this.displayMessage()}
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.container}>
<MultiSelectList
style={{backgroundColor: '#ffe4c4'}}
data={this.state.items}
renderItem={this.renderListItems}
numColumns={2}
contentContainerStyle={{}}
onEndReachedThreshold={0.5}
maxToRenderPerBatch={2}
initialNumToRender={4}
ListHeaderComponent={this.renderHeader}
getItemLayout={(data, index) => (
{length: Dimensions.get('window').height/2, offset: Dimensions.get('window').height/2 * index, index}
)}
backgroundColor={this.state.backgroundColor}
//contentContainerStyle={{backgroundColor: '#1e4683'}}
/>
</View>
</TouchableWithoutFeedback>
</View>
);
}
displayMessage() {
if(this.state.showRespondTo) {
console.log('this.state.showRespondTo:', this.state.showRespondTo);
return ( <RespondToInquiry/> )
}
}
This is what RespondToInquiry is:
import React, { Component } from 'react'
import {
Text,
TextInput,
TouchableOpacity,
TouchableHighlight,
TouchableWithoutFeedback,
View,
Dimensions,
Keyboard
} from 'react-native'
import { Calendar, CalendarList, Agenda } from 'react-native-calendars';
import { Map } from 'immutable';
import Modal from 'react-native-modal';
import firebase from 'react-native-firebase';
export default class RespondToInquiry extends React.PureComponent {
constructor() {
super()
this.state = {
_markedDates: {},
//_selectedDay: new Date().dateString,
modalVisible: false,
message: 'Hi, I would like to rent an item from you.',
rentButtonBackground: '#6de3dc',
datesArray: []
}
this.onDayPress = this.onDayPress.bind(this)
}
/*initialState = {
[new Date()]: { 'selected': false,
customStyles: {
container: {
backgroundColor: '#6de3dc',
},
text: {
color: 'white',
fontWeight: 'bold'
},
},
}
}*/
showCalendar = () => {
return (
<Calendar
style={{
borderWidth: 0,
borderRadius: 4,
}}
theme={{
todayTextColor: '#6de3dc',
selectedDayBackgroundColor: '#6de3dc',
}}
markingType={'custom'}
markedDates={this.state._markedDates}
// Initially visible month. Default = Date()
// Minimum date that can be selected, dates before minDate will be grayed out. Default = undefined
minDate={new Date()}
// Maximum date that can be selected, dates after maxDate will be grayed out. Default = undefined
// Handler which gets executed on day press. Default = undefined
onDayPress={day => this.onDayPress(day)}
// Handler which gets executed on day long press. Default = undefined
onDayLongPress={day => {
console.log('selected day', day)
}}
// Month format in calendar title. Formatting values: http://arshaw.com/xdate/#Formatting
monthFormat={'MMM d, yyyy'}
// Handler which gets executed when visible month changes in calendar. Default = undefined
onMonthChange={month => {
console.log('month changed', month)
}}
// Hide month navigation arrows. Default = false
//hideArrows={true}
// Replace default arrows with custom ones (direction can be 'left' or 'right')
//renderArrow={(direction) => (<Arrow />)}
// Do not show days of other months in month page. Default = false
hideExtraDays={true}
// If hideArrows=false and hideExtraDays=false do not switch month when tapping on greyed out
// day from another month that is visible in calendar page. Default = false
//disableMonthChange={true}
// If firstDay=1 week starts from Monday. Note that dayNames and dayNamesShort should still start from Sunday.
firstDay={0}
// Hide day names. Default = false
//hideDayNames={true}
// Show week numbers to the left. Default = false
//showWeekNumbers={true}
// Handler which gets executed when press arrow icon left. It receive a callback can go back month
onPressArrowLeft={substractMonth => substractMonth()}
// Handler which gets executed when press arrow icon left. It receive a callback can go next month
onPressArrowRight={addMonth => addMonth()}
/>
)
}
onDayPress = (day) => {
const _selectedDay = day.dateString;
let marked = true;
if (this.state._markedDates[_selectedDay]) {
// Already in marked dates, so reverse current marked state
marked = !this.state._markedDates[_selectedDay].selected;
console.log('marked:', marked);
// Create a new object using object property spread since it should be immutable
// Reading: https://davidwalsh.name/merge-objects
const updatedMarkedDates = {...this.state._markedDates, ...{ [_selectedDay]: { 'selected': marked,
customStyles: {
container: {
backgroundColor: '#6de3dc',
},
text: {
color: 'white',
fontWeight: 'bold'
},
},
} } }
// Triggers component to render again, picking up the new state
this.setState({ _markedDates: updatedMarkedDates }, () => {
console.log('updatedMarkedDates:', this.state._markedDates);
});
}
else {
// Create a new object using object property spread since it should be immutable
// Reading: https://davidwalsh.name/merge-objects
const updatedMarkedDates = {...this.state._markedDates, ...{ [_selectedDay]: { 'selected': true,
customStyles: {
container: {
backgroundColor: '#6de3dc',
},
text: {
color: 'white',
fontWeight: 'bold'
},
},
} } }
// Triggers component to render again, picking up the new state
this.setState({ _markedDates: updatedMarkedDates }, () => {
console.log('updatedMarkedDates:', this.state._markedDates);
});
}
}
waitToStoreDates = () => new Promise((resolve) => {
let x = 0;
let datesArray = [];
for(date in this.state._markedDates) {
console.log("Date Object: ",date);
if(this.state._markedDates[date].selected) {
datesArray.push(date);
}
x++;
}
if(x == Object.keys(this.state._markedDates).length) {
console.log("x:",x);
console.log('datesArray in waitToStoreDates:', datesArray);
this.state.datesArray = datesArray;
resolve();
}
})
async processMarkedDates() {
await this.waitToStoreDates();
}
setModalVisible(visible) {
this.setState({ modalVisible: visible })
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log('componentDidUpdate in InitiateRent:', this.props, prevProps, prevState, snapshot)
}
_renderModalContent = () => (
<TouchableWithoutFeedback onPress={() => {console.log('tapped')}}>
<View
style={{
paddingTop: 5,
paddingBottom: 10,
paddingLeft: 10,
paddingRight: 10,
marginTop: 0,
flex: 1,
width: Dimensions.get('window').width
}}>
<View style={{ flexDirection: 'column', justifyContent: 'space-between', flex: 1 }}>
<View style={{ flexDirection: 'column', flex: 1 }}>
<Text
style={{
flex: 0,
width: Dimensions.get('window').width,
color: 'white',
fontWeight: '700',
marginBottom: 5,
}}>
Date(s) Needed:
</Text>
{this.showCalendar()}
</View>
<View style={{ flexDirection: 'column', flex: 0.2, marginBottom: 10 }}>
<TextInput
style={{
width: 280,
flex: 1,
borderColor: 'gray',
borderWidth: 1,
backgroundColor: '#ffffff',
paddingLeft: 5,
borderRadius: 4,
}}
onChangeText={text => this.setState({ message: text })}
value={this.state.message}
multiline={true}
numberOfLines={2}
/>
</View>
<View style={{ flex: 0.1, borderRadius: 4, borderWidth: 0 }}>
<TouchableOpacity
activeOpacity={1}
style={{
backgroundColor: this.state.rentButtonBackground,
flex: 1,
justifyContent: 'center',
alignItems: 'center',
width: 280,
borderRadius: 4,
borderWidth: 0,
}}
onPress={() => {
this.setState({ rentButtonBackground: '#94ebe6' })
setTimeout(() => {
this.setState({ rentButtonBackground: '#6de3dc' })
let timestamp = new Date().getTime().toString();
this.processMarkedDates();
console.log("this.state.datesArray", this.state.datesArray);
dataChat = {
"title": "Rental Inquiry",
"lastMessage": this.state.message,
"timestamp": timestamp
}
dataMessage = {}
dataMessage[timestamp] = {
"name": "eamon",
"message": this.state.message,
"timestamp": timestamp,
"dates": JSON.stringify(this.state.datesArray)
};
this.sendRentMessage(dataChat, dataMessage, timestamp)
this.setModalVisible(false)
}, 1)
}}>
<Text
style={{
backgroundColor: this.state.rentButtonBackground,
textAlign: 'center',
color: 'white',
fontWeight: '900',
fontSize: 18,
borderRadius: 4,
borderWidth: 0,
}}>
SEND
</Text>
</TouchableOpacity>
</View>
</View>
</View>
</TouchableWithoutFeedback>
);
componentDidMount() {
firebase.messaging()
.hasPermission()
.then(enabled => {
if (!enabled) {
console.log('permissions disabled');
this._getPermission();
}
console.log('permissions enabled');
firebase.messaging().subscribeToTopic('all').catch((error) => {alert(error)});
firebase.messaging().getToken()
.then(fcmToken => {
if (fcmToken) {
//USE THIS FOR INDIVIDUAL DEVICE MESSAGES?
console.log(fcmToken);
} else {
alert("User doesn't have a token yet");
}
}).catch((error) => {
alert(error);
});
}).then(() => {
}).catch((error) => {alert(error)});
}
_getPermission = () => {
firebase.messaging()
.requestPermission()
.catch(error => {
// User has rejected permissions
// this._getPermission();
Alert.alert(
'ERROR',
"You must enable push notifications for the messaging system to work! If you don't you won't be able to use SnagIt! Please enable notificaitons in your phone - go to: Settings > Notifications > SnagIt.",
[
{text: 'OK', onPress: () => console.log('OK Pressed')},
],
{ cancelable: false }
)
});
}
sendRentMessage(dataChat, dataMessage, timestamp) {
// Add a new document with a generated id. //user-user //send generated ID and then change to message id in cloud
/*let addChat = firebase.firestore().collection('chats').doc(timestamp);
// Add a new document with a generated id. //user-user //send generated ID and then change to message id in cloud
let addMessage = firebase.firestore().collection('messages').doc(timestamp);
// Set the 'capital' field of the city
addChat.update(dataChat).then(() => {
// Set the 'capital' field of the city
addMessage.update(dataMessage).catch((error) => {
//alert(error);
addMessage.set(dataMessage).catch((error) => {
alert(error);
});
});
}).catch((error) => {
//alert(error);
addChat.set(dataChat).catch((error) => {
alert(error);
}).then(() => {
addMessage.update(dataMessage).catch((error) => {
//alert(error);
addMessage.set(dataMessage).catch((error) => {
alert(error);
});
});
})
});*/
}
render() {
return (
<View style={{flex: 1}}>
<Modal
animationType="slide"
transparent={true}
visible={this.state.modalVisible}
onBackdropPress ={() => {console.log("backdrop pressed"); this.setModalVisible(false)}}>
{this._renderModalContent()}
</Modal>
</View>
)
}
}
For console output I see:
this.state.showRespondTo: – true
So I know that the conditional is changing to the state where it should render the component. I also see half of the screen (where the component should be) as blank/white background. This is because of how I have my flexbox setup, and is the behavior I want. But the element that is supposed to be rendered isn't showing up. The state of the conditional changes on a didFocus listener, like this:
componentDidMount() {
this._sub = this.props.navigation.addListener(
'didFocus',
() => {
if(this.props.navigation.getParam('data', '') != '') {
console.log('showRespondTo fired.');
this.setState({showRespondTo: true});
}
}
);
....
}
Why doesn't my InitiateRent component render where the blank/white area is (see image below)?
Just something silly, this.state.modalVisible is set to false initially so the modal wasn't showing up.
Working on a react-native app that records coordinates of where a user walked in a 'memory', and storing the memories (with a description) and a set of coordinates in Realm.
I've used ListView in a project with a different database, but using the realm-listview I'm unable to get the descriptions of the memories rendered on a page.
Storing my realm setup in one file that looks like the below. Main action is storing the data in the Memory/Coordinate Schemas, and getting the data to use in the ListView using the RealObjects.countMemories() function. It is correctly returning an array of realm objects when I look at it in the console.
export default class RealmObjects {}
RealmObjects.MemorySchema = {
name: 'Memory',
properties: {
description: 'string',
coords: {type: 'list', objectType: 'Coordinate'},
}
}
RealmObjects.CoordinateSchema = {
name: 'Coordinate',
properties: {
longitude: 'double',
latitude: 'double',
timeStamp: 'double'
}
}
RealmObjects.saveMemory = function(coordinates, description) {
console.log('---------------realm write-------------')
console.log(description)
console.log(coordinates)
realm.write(() => {
let memory = realm.create('Memory', {
description: description,
id: 1
});
let coordList = memory.coords
for (var i = 0; i < coordinates.length; i++) {
console.log(i)
console.log(coordinates[i].timeStamp)
coordList.push({longitude: coordinates[i].longitude,
latitude: coordinates[i].latitude,
timeStamp: coordinates[i].timeStamp});
console.log(memory.coords[i].timeStamp)
}
});
}
RealmObjects.countMemories = function() {
console.log(realm.objects('Memory'));
return realm.objects('Memory');
}
import Realm from 'realm';
let realm = new Realm({schema: [RealmObjects.MemorySchema, RealmObjects.CoordinateSchema]});
In my component, I'm importing the ListView component like this: import { ListView } from 'realm/react-native';.
Constructor setup like this:
constructor() {
super();
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows(RealmObjects.countMemories()),
};
console.log('datasource')
console.log(this.state.dataSource)
}
Render function setup like this:
render() {
return (
<View style={styles.container}>
<TouchableHighlight style={styles.buttonLeft} onPress={() => this.goBack()}>
<Text>Back</Text>
</TouchableHighlight>
<ListView style={styles.listview}
dataSource={this.state.dataSource}
renderRow={(rowData) => <Text>something: {rowData.description}</Text>}
/>
</View>
);
}
This render method will not produce anything in the ListView. For now, I'm just trying to get a list of the descriptions of the memories to display on screen.
New to both react-native and realm, so not sure if my problems reside here or somewhere else. Any help is appreciated!
EDIT: Changed the render row and row data items to look like this to log data, which logs the correct descriptions of each row in the console, but still does not display anything on the screen:
renderRow(rowData) {
console.log('my data');
console.log(rowData.description);
return (
<Text>{rowData.description}</Text>
);
}
render() {
return (
<View style={styles.container}>
<TouchableHighlight style={styles.buttonLeft} onPress={() => this.goBack()}>
<Text>Back</Text>
</TouchableHighlight>
<ListView style={styles.listview}
dataSource={this.state.dataSource}
renderRow={(rowData) => this.renderRow(rowData)}
/>
</View>
);
}
EDIT: Current styles:
const styles = StyleSheet.create({
button: {
paddingHorizontal: 12,
alignItems: 'center',
marginHorizontal: 10,
top: 50,
backgroundColor: 'rgba(236,64,122,0.7)',
paddingVertical: 12,
},
listview: {
top: 100,
backgroundColor: 'rgba(236,64,122,0.7)',
},
container: {
flex: 1,
flexDirection: 'column',
marginTop: 60,
},
buttonRight: {
position: 'absolute',
top: 0,
right: 5,
backgroundColor: 'rgba(236,64,122,0.7)',
paddingHorizontal: 18,
paddingVertical: 12,
borderRadius: 20,
},
buttonLeft: {
position: 'absolute',
top: 0,
left: 5,
backgroundColor: 'rgba(236,64,122,0.7)',
paddingHorizontal: 18,
paddingVertical: 12,
borderRadius: 20,
}
});
I too had faced the same problem. But I converted the response from the realm into array and then passed it to flat list and it worked like a charm.
async getAllPrinterArray() {
realm = await this.getRealMObject();
favoritePrinterArray = realm.objects(constants.SCHEMA_SAVE_PRINTERS);
console.log(favoritePrinterArray);
if (favoritePrinterArray.length > 0) {
this.setState({ enable: false });
arr4 = Array.from(favoritePrinterArray);
}
return arr4;
}
<FlatList
data={printerObject}
numColumns={2}
renderItem={({ item }) => this.renderListRow(item)}
/>
I'd like the style of a button in my app to change when it is being pressed. What is the best way to do this?
Use TouchableHighlight.
Here an example:
import React from 'react';
import { TouchableHighlight, View, Text, StyleSheet } from 'react-native';
export default function Button() {
var [ isPress, setIsPress ] = React.useState(false);
var touchProps = {
activeOpacity: 1,
underlayColor: 'blue', // <-- "backgroundColor" will be always overwritten by "underlayColor"
style: isPress ? styles.btnPress : styles.btnNormal, // <-- but you can still apply other style changes
onHideUnderlay: () => setIsPress(false),
onShowUnderlay: () => setIsPress(true),
onPress: () => console.log('HELLO'), // <-- "onPress" is apparently required
};
return (
<View style={styles.container}>
<TouchableHighlight {...touchProps}>
<Text>Click here</Text>
</TouchableHighlight>
</View>
);
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
btnNormal: {
borderColor: 'blue',
borderWidth: 1,
borderRadius: 10,
height: 30,
width: 100,
},
btnPress: {
borderColor: 'blue',
borderWidth: 1,
height: 30,
width: 100,
}
});
React Native now provides a new Pressable component that can detect various stages of press interactions.
So, in order to change the color(in general any style) of the component, refer below example:
<Pressable
style={({ pressed }) => [{ backgroundColor: pressed ? 'black' : 'white' }, styles.btn ]}>
{({ pressed }) => (
<Text style={[{ color: pressed ? 'white' : 'black' }, styles.btnText]}>
{text}
</Text>
)}
</Pressable>
Code breakdown:
style={({ pressed }) => [{ backgroundColor: pressed ? 'black' : 'white' }, styles.btn ]}
Here the style prop receives pressed(boolean) that reflects whether Pressable is pressed or not and returns an array of styles.
{({ pressed }) => (
<Text style={[{ color: pressed ? 'white' : 'black' }, styles.btnText]}>
{text}
</Text>
)}
Here the text style too can be modified as the pressed is also accessible to the children of Pressable component.
Use the prop:
underlayColor
<TouchableHighlight style={styles.btn} underlayColor={'gray'} />
https://reactnative.dev/docs/touchablehighlight
This is Besart Hoxhaj's answer in ES6. When i answer this, React Native is 0.34.
import React from "react";
import { TouchableHighlight, Text, Alert, StyleSheet } from "react-native";
export default class TouchableButton extends React.Component {
constructor(props) {
super(props);
this.state = {
pressed: false
};
}
render() {
return (
<TouchableHighlight
onPress={() => {
// Alert.alert(
// `You clicked this button`,
// 'Hello World!',
// [
// {text: 'Ask me later', onPress: () => console.log('Ask me later pressed')},
// {text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'},
// {text: 'OK', onPress: () => console.log('OK Pressed')},
// ]
// )
}}
style={[
styles.button,
this.state.pressed ? { backgroundColor: "green" } : {}
]}
onHideUnderlay={() => {
this.setState({ pressed: false });
}}
onShowUnderlay={() => {
this.setState({ pressed: true });
}}
>
<Text>Button</Text>
</TouchableHighlight>
);
}
}
const styles = StyleSheet.create({
button: {
padding: 10,
borderColor: "blue",
borderWidth: 1,
borderRadius: 5
}
});
use something like that :
class A extends React.Component {
constructor(props){
super(props);
this.state = {
onClicked: false
}
this.handlerButtonOnClick = this.handlerButtonOnClick.bind(this);
}
handlerButtonOnClick(){
this.setState({
onClicked: true
});
}
render() {
var _style;
if (this.state.onClicked){ // clicked button style
_style = {
color: "red"
}
}
else{ // default button style
_style = {
color: "blue"
}
}
return (
<div>
<button
onClick={this.handlerButtonOnClick}
style={_style}>Press me !</button>
</div>
);
}
}
If you use an external CSS, you can use className in place of style property :
render() {
var _class = "button";
var _class.concat(this.state.onClicked ? "-pressed" : "-normal") ;
return (
<div>
<button
onClick={this.handlerButtonOnClick}
className={_class}>Press me !</button>
</div>
);
}
It doesn't really matter how do you apply your CSS. Keep your eyes on the "handlerButtonOnClick" method.
When the state change, the component is re-rendered ("render" method is called again).
Good luck ;)