Change button style on press in React Native - javascript

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 ;)

Related

Progressively focus TextInput react native

I am currently creating a 4 digit otp (one time password) view for a react native app and i need the TextInput to progressively focus when i type a number on the previous input.
I have been able to make the style change (without focus) but the focus doesn't change with it and when i enter another digit the app crashes.
I want it such that when i type in on the first input, the focus move to the next TextInput and so on and on the last input, it focuses on submit.
How do i go about this?
Below is my code
the component
import React, { useState, useEffect } from 'react';
import { StyleSheet, Text, ScrollView, View, Alert, Image } from 'react-native';
import { SimpleLinearGradientButton } from '../../components/Buttons';
import { SimpleCard } from '../../components/Cards';
export default function(props) {
const [otp, setOtp] = useState([]);
const [isFocused, setIsFocused] = useState({ focused: true, index: 0 });
const inputs = Array(4).fill(0);
function renderInputs() {
return inputs.map((input, index) => {
return (
<TextInput
style={
isFocused.focused && index === isFocused.index
? styles.inputFieldsFocused
: styles.inputFields
}
key={index}
keyboardType={'numeric'}
onChange={focus}
></TextInput>
);
});
}
function focus(e) {
setOtp(otp.concat(this.value));
setIsFocused({ focused: true, index: isFocused.index + 1 });
isFocused.index ? e.focus() : null;
}
return (
<ScrollView contentContainerStyle={styles.content}>
<View>
<Image
style={styles.image}
source={require('../../../assets/images/verification.png')}
/>
</View>
<View>
<Text>Verification</Text>
<Text>
Enter the 4 digit sent to your email address
</Text>
</View>
<View>
<SimpleCard>
<>{renderInputs()}</>
</SimpleCard>
<SimpleLinearGradientButton
title="Submit"
/>
</View>
</ScrollView>
);
}
The focus function was meant to focus the next input at that index but this doesn't seem to work but the style changes. How do i go about this? Thanks
I suggest to use single hidden TextInput, and render digits in simple View-s, based on input's value. I see no reason to have different inputs for 4 digit code.
However if you need to use different inputs, you have to control their focus states in imperative way (with refs). See focus() method. Also there are some bugs with this method (https://github.com/facebook/react-native/issues/19366)
See docs here https://facebook.github.io/react-native/docs/textinput
tl;dr Set maxLength of textInput to 1 and on onChangeText callback change to next textInput using refs and focus()
This is a straight-forward implementation to understand easily. You can reduce the code a lot better. And I guess you're supposed to implement Backspace to clear the text and go back to previous textinput. I used onKeyPress prop to achieve that.
import React, { Component } from 'react';
import { Text, View, StyleSheet, TextInput } from 'react-native';
import Constants from 'expo-constants';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
one: '',
two: '',
three: '',
oneFocus: false,
twoFocus: false,
threeFocus: false,
};
}
componentDidMount() {
this.refs.one.focus();
}
handleChangeTextOne = (text) => {
this.setState({ one: text }, () => { if (this.state.one) this.refs.two.focus(); });
}
handleChangeTextTwo = (text) => {
this.setState({ two: text }, () => { if (this.state.two) this.refs.three.focus(); });
}
handleChangeTextThree = (text) => {
this.setState({ three: text });
}
backspace = (id) => {
if (id === 'two') {
if (this.state.two) { this.setState({ two: '' }); } else if (this.state.one) { this.setState({ one: '' }); this.refs.one.focus(); }
} else if (id === 'three') {
if (this.state.three) { this.setState({ three: '' }); } else if (this.state.two) { this.setState({ two: '' }); this.refs.two.focus(); }
}
}
render() {
const { oneFocus, twoFocus, threeFocus } = this.state;
const oneStyle = {
borderBottomColor: oneFocus ? 'red' : 'black',
borderBottomWidth: oneFocus ? 2 : 1,
};
const twoStyle = {
borderBottomColor: twoFocus ? 'red' : 'black',
borderBottomWidth: twoFocus ? 2 : 1,
};
const threeStyle = {
borderBottomColor: threeFocus ? 'red' : 'black',
borderBottomWidth: threeFocus ? 2 : 1,
};
return (
<View style={styles.container}>
<View style={styles.inputcontainer}>
<TextInput
ref='one'
style={[styles.textInput, { ...oneStyle }]}
autoCorrect={false}
autoCapitalize='none'
keyboardType='number-pad'
caretHidden
onFocus={() => this.setState({ oneFocus: true })}
onBlur={() => this.setState({ oneFocus: false })}
maxLength={1}
onChangeText={(text) => { this.handleChangeTextOne(text); }}
value={this.state.one}
/>
<TextInput
ref='two'
onKeyPress={({ nativeEvent }) => (
nativeEvent.key === 'Backspace' ? this.backspace('two') : null
)}
style={[styles.textInput, { ...twoStyle }]}
autoCorrect={false}
autoCapitalize='none'
maxLength={1}
onFocus={() => this.setState({ twoFocus: true })}
onBlur={() => this.setState({ twoFocus: false })}
caretHidden
keyboardType='number-pad'
onChangeText={(text) => { this.handleChangeTextTwo(text); }}
value={this.state.two}
/>
<TextInput
ref='three'
onKeyPress={({ nativeEvent }) => (
nativeEvent.key === 'Backspace' ? this.backspace('three') : null
)}
style={[styles.textInput, { ...threeStyle }]}
autoCorrect={false}
autoCapitalize='none'
onFocus={() => this.setState({ threeFocus: true })}
onBlur={() => this.setState({ threeFocus: false })}
maxLength={1}
caretHidden
keyboardType='number-pad'
onChangeText={(text) => { this.handleChangeTextThree(text); }}
value={this.state.four}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
inputcontainer: {
height: '5%',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'flex-start',
paddingHorizontal: '20%',
marginBottom: '2%',
},
textInput: {
fontSize: 22,
textAlign: 'center',
paddingVertical: 0,
paddingHorizontal: 0,
width: '12%',
},
});
Run this in snack: https://snack.expo.io/#legowtham/otp-textinput-example-react-native

React Native - can't get view to render with conditional

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.

E-commerce like feature for selecting product sizes or color

I have searched the whole web to the best of my ability on how to create an interface for selecting products just like the ones implemented in most of the major e-commerce apps(amazon,taobao,shopify..).The goal is to highlight or change the styles of the selected item in the list by simultaneously removing the styles of the previously selected item. I am trying to use react-native to clone such a feature. Any references or guides on how to do this will be highly appreciated!
import React, { Component } from 'react';
import { View, Text ,StyleSheet, TouchableOpacity, } from 'react-native';
class Selections extends Component {
state={
highlighted: false,
id: null
}
// The purpose of this function is to set the state to the target index on press
indexStateHandler = (i) =>{
if(this.state.id === null){
this.setState({
id: i
})
}else{
this.setState({
id:i
})
}
console.log("index: "+i)
console.log("state: "+this.state.id)
}
//The purpose of this function is to set styles for the targeted index
highlightStateHandler = (i) =>{
if(this.state.id === i){
if(!this.state.highlighted){
this.setState({
highlighted:true
})
}
else{
this.setState({
highlighted:false
})
}
}
}
highlightHandler = (i) =>{
// console.log(i)
this.indexStateHandler(i)
this.highlightStateHandler(i)
}
render() {
return (
<View style={styles.selectionWrapper}>
<View style={styles.label}><Text style={{color: "black"}}>{this.props.label}</Text></View>
<View style={styles.listContainer}>
{this.props.options.map((options, i) => (
<TouchableOpacity onPress={()=>this.highlightHandler(i)} key={i}>
<View style={this.state.highlighted&&this.state.id == i?styles.highlighted:styles.options} > <Text style={styles.text}>{options}</Text> </View>
</TouchableOpacity>
)
)}
</View>
</View>
);
}
}
const styles= StyleSheet.create({
selectionWrapper:{
width: '100%',
height: 100,
borderWidth: 1,
borderColor: 'red',
},
label:{
flex: 1,
}
,
listContainer:{
flex: 3,
flexDirection: "row",
justifyContent: "space-around",
alignItems: 'center',
// backgroundColor: "#7fffd4"
},
options:{
borderRadius: 10,
padding: 5,
borderWidth: 1,
borderColor: "#d0b783",
// backgroundColor: ""
},
text:{
color: 'black',
textAlign: 'center'
},
highlighted:{
borderRadius: 10,
padding: 5,
borderWidth: 1,
// borderColor: "#d0b783",
backgroundColor: "#d0b783"
}
})
export default Selections
.....
.....
.....
<TouchableOpacity
style={[styles.buttonStyle,{
backgroundColor : item.id === this.state.selectedItem.id ? "red" : "blue"
}]}
>
{
...
...
}
</TouchableOpacity>
.....
.....
.....
Have a look on TouchableOpacity and TouchableHighlight and try to run examples to see how they work. Also you can combine them to changing visual changes by changing styles.
import React, { Component } from 'react' import { AppRegistry, StyleSheet, TouchableOpacity, Text, View, } from 'react-native'
export default class App extends React.Component { constructor(props) {
super(props)
this.state = {
itemEn1: true,
itemEn2: false,
itemEn3: false,
} }
onPress1 = () => {
this.setState({
itemEn1: true,
itemEn2: false,
itemEn3: false,
}) }
onPress2 = () => {
this.setState({
itemEn1: false,
itemEn2: true,
itemEn3: false,
}) }
onPress3 = () => {
this.setState({
itemEn1: false,
itemEn2: false,
itemEn3: true,
}) }
render() {
return (
<View style={styles.container}>
<TouchableOpacity
style={this.state.itemEn1 ? styles.buttonEnabled : styles.buttonDisabled}
onPress={this.onPress1}>
<Text> Item 1 </Text>
</TouchableOpacity>
<TouchableOpacity
style={this.state.itemEn2 ? styles.buttonEnabled : styles.buttonDisabled}
onPress={this.onPress2}>
<Text> Item 2 </Text>
</TouchableOpacity>
<TouchableOpacity
style={this.state.itemEn3 ? styles.buttonEnabled : styles.buttonDisabled}
onPress={this.onPress3}>
<Text> Item 3 </Text>
</TouchableOpacity>
</View>
) } }
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 10
},
buttonDisabled: {
alignItems: 'center',
backgroundColor: '#DDDDDD',
padding: 10,
marginTop: 20
},
buttonEnabled: {
alignItems: 'center',
backgroundColor: 'green',
padding: 10,
marginTop: 20
},
countContainer: {
alignItems: 'center',
padding: 10
},
})
this is by map function:
export default class DataCollector extends React.Component {
constructor(props) {
super(props);
this.SendBack = this.SendBack.bind(this);
}
SendBack(info) {
console.log("Key is :", info);
this.props.callback(info);
}
render() {
let testData = this.props.data.map(function (articleData, index) {
return (
<View key={index}>
<TouchableOpacity
activeOpacity={0.6}
key={index}
onPress={() => this.callBack([articleData.id, articleData.name])}>
</TouchableOpacity>
</View>
)
}, this);
return (
<View>
{testData}
</View>
);
}
}
so now you have access to the clicked item and can use it for enable/disable etc..

React Native and Refs - Why does this function not return my content?

I have the following code in react native:
'use strict'
import React, { Component } from 'react';
import { AppRegistry, Animated, Dimensions, Easing, Image, Text, View, StyleSheet, TouchableOpacity, ScrollView } from 'react-native';
const categoryTwoLinks = [
{ label: 'Subcategory1' },
{ label: 'Subcategory2' },
{ label: 'Subcategory3' },
{ label: 'Subcategory4' },
{ label: 'Subcategory5' },
{ label: 'Subcategory6' },
]
const categoryTouts = [
{ image: require('./assets/images/image01.jpg'), title: 'Category1' },
{ image: require('./assets/images/image02.jpg'), title: 'Category2', links: categoryTwoLinks },
{ image: require('./assets/images/image03.jpg'), title: 'Category3' },
]
class Tout extends Component {
render() {
console.log('tout')
return (
<Image
source={this.props.image}
ratioGrow
alignItems='center'
style={{ alignItems: 'center', justifyContent: 'center' }}
>
<Text
style={{ color: 'white', fontSize: 24, backgroundColor: 'transparent', fontWeight: 'bold' }}
>
{this.props.title}
</Text>
</Image>
)
}
}
export default class scrollAccordion extends Component {
handleScroll = ({ nativeEvent: { contentOffset } }: Object) => {
const scrollY = contentOffset.y
this.scrollOffsetValue.setValue(scrollY)
}
setScrollRef = node => {
console.log('set scroll ref')
if (node) {
this.scrollRef = node
}
}
renderCategoryTouts = () => {
console.log('render category tout')
categoryTouts.map((tout, index) => {
return (
<Tout
key={tout.title}
{...tout}
scrollViewRef={this.state.scrollRef}
scrollOffset={this.scrollOffsetValue}
/>
)
})
}
render() {
console.log('render')
let categoryTouts = this.scrollRef ? this.renderCategoryTouts() : null
return (
<View style={styles.container}>
<ScrollView
scrollEventThrottle={1}
ref={this.setScrollRef}
onScroll={this.handleScroll}
>
<View>
{categoryTouts}
</View>
</ScrollView>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingTop: 40,
backgroundColor: 'white',
},
toutText: {
color: 'white', backgroundColor: 'transparent', fontWeight: 'bold', fontSize: 24
}
});
AppRegistry.registerComponent('scrollAccordion', () => scrollAccordion);
which is not returning any content from the renderCategoryTouts function.
I know that initially, this.scrollRef is undefined, so the main render function will return {null} for the {categoryTouts} variable. But after the scrollRef is set, I need it to return the results of renderCategoryTouts. How do I get this to work?
In renderCategoryTouts function of yours you have a variable categoryTouts variabke. You have never declared a variable like that in this function so I assume it is a global variable.
renderCategoryTouts = () => {
categoryTouts.map((tout, index) => {
return (
<Tout
key={tout.title}
{...tout}
scrollViewRef={this.state.scrollRef}
scrollOffset={this.scrollOffsetValue}
/>
)
})
}
And in this function you're using this.state.scrollRef, this.scrollOffsetValue so you need to bind this function.(I suggest you do that in the constructor of class)
this.renderCategoryTouts = this.renderCategoryTouts.bind(this)
also in case this does not work full code would be very helpful

Focus style for TextInput in react-native

In React Native, how do you change the style of a textInput when it gets focus? Say I have something like
class MyInput extends Component {
render () {
return <TextInput style={styles.textInput} />;
}
};
const stylesObj = {
textInput: {
height: 50,
fontSize: 15,
backgroundColor: 'yellow',
color: 'black',
}
};
const styles = StyleSheet.create(stylesObj);
And I want to change the background color on focus to green.
This documentation leads me to believe that the solution is something like
class MyInput extends Component {
constructor (props) {
super(props);
this.state = {hasFocus: false};
}
render () {
return (<TextInput
style={this.state.hasFocus ? styles.focusedTextInput : styles.textInput}
onFocus={this.setFocus.bind(this, true)}
onBlur={this.setFocus.bind(this, false)}
/>);
}
setFocus (hasFocus) {
this.setState({hasFocus});
}
};
const stylesObj = {
textInput: {
height: 50,
fontSize: 15,
backgroundColor: 'yellow',
color: 'black',
}
};
const styles = StyleSheet.create({
...stylesObj,
focusedTextInput: {
...stylesObj,
backgroundColor: 'green',
}
});
Ignoring potential mistakes in the styles structuring, would this be considered correct way to handle it? It seems very verbose to me.
You can achieve this by passing in the onFocus and onBlur events to set and unset styles when focused and blurred:
onFocus() {
this.setState({
backgroundColor: 'green'
})
},
onBlur() {
this.setState({
backgroundColor: '#ededed'
})
},
And then, in the TextInput do this:
<TextInput
onBlur={ () => this.onBlur() }
onFocus={ () => this.onFocus() }
style={{ height:60, backgroundColor: this.state.backgroundColor, color: this.state.color }} />
I've set up a full working project here. I hope this helps!
https://rnplay.org/apps/hYrKmQ
'use strict';
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
Text,
View,
TextInput
} = React;
var SampleApp = React.createClass({
getInitialState() {
return {
backgroundColor: '#ededed',
color: 'white'
}
},
onFocus() {
this.setState({
backgroundColor: 'green'
})
},
onBlur() {
this.setState({
backgroundColor: '#ededed'
})
},
render: function() {
return (
<View style={styles.container}>
<TextInput
onBlur={ () => this.onBlur() }
onFocus={ () => this.onFocus() }
style={{ height:60, backgroundColor: this.state.backgroundColor, color: this.state.color }} />
</View>
);
}
});
var styles = StyleSheet.create({
container: {
flex: 1,
marginTop:60
}
});
AppRegistry.registerComponent('SampleApp', () => SampleApp);
The best way to control the style when the element is focused / blurred is to create your own TextInput wrapper
export const MyAppTextInput = (props) => {
return (
<TextInput
{...props}
/>
);
};
Note that {...props} will pass in any property you already set or available for TextInput.
Then extend the above component by adding state to apply styles when focus / blur
export const MyAppTextInput = (props) => {
const [isFocused, setIsFocused] = useState(false);
return (
<TextInput
{...props}
style={[props.style, isFocused && {borderWidth: 5, borderColor: 'blue'}]}
onBlur={() => setIsFocused(false)}
onFocus={() => setIsFocused(true)}
/>
);
};
And remember when you use the component to bind the value like in the example (see value={passwordText}); otherwise the value will disappear on blur as a new render commences after the state change.
<MyAppTextInput
style={styles.input}
value={passwordText}
textContentType="password"
autoCompleteType="off"
secureTextEntry
onChangeText={text => {
setPasswordText(text);
}}
/>
You can of course avoid creating a wrapper but if you have more than one input it will create a mess in your input(s) parent components as you will have to add repeating logic.
Use refs, DirectManipulation and setNativeProps for more performance: https://facebook.github.io/react-native/docs/direct-manipulation.
class MyInput extends Component {
focusedInput = () => {
this.textInput.setNativeProps({
style: { backgroundColor: 'green' }
})
}
blurredInput = () => {
this.textInput.setNativeProps({
style: { backgroundColor: 'yellow' }
})
}
render () {
return <TextInput
ref={c => { this.textInput = c}}
style={styles.textInput}
onFocus={this.focusedInput}
onBlur={this.blurredInput} />
}
}
const stylesObj = {
textInput: {
height: 50,
fontSize: 15,
backgroundColor: 'yellow',
color: 'black',
}
}
const styles = StyleSheet.create(stylesObj)
This updates the TextInput component directly without re-rendering the component hierarchy.
You can create a state to keep track of the input-state and use that state to toggle the style. Here is a simple example
const App = () => {
const [isActive, setActive] = useState(false);
return (
<TextInput style={{ color: isActive ? 'black' : 'grey' }} onFocus={() => setActive(true)} onBlur={() => setActive(false)}/>
);
}
Nader Dabit´s pointed me to do something similar -- using the state for styles -- but I think it can be done in a cleaner way if you created separate styles for the focused and unfocused style and pass only the style ID as follows:
getInitialState() {
return { style: styles.textinput_unfocused }
}
onFocus() {
this.setState({ style: styles.textinput_focused })
}
onBlur() {
this.setState({ style: styles.textinput_unfocused })
}
in render -- referencing by styleID in this.state.style, note how the different styles are passed as an Array:
<TextInput
onBlur={ () => this.onBlur() }
onFocus={ () => this.onFocus() }
style={ [styles.textinput, this.state.style] } />
+ your stylesheet à la carte:
textinput_focused: {
backgroundColor: 'red',
color: 'white'
}
textinput_unfocused: {
backgroundColor: 'green'
}
Hey guys I kinda used everyones idea :p
#Felix gave me an idea that might be perhaps even cleaner. (I would have loved to not have included state though on this static component, just to change styling... But I am to new to this to figure that out.
here was my solution:
import React, { Component } from 'react';
import { StyleSheet, TextInput } from 'react-native';
class TxtInput extends Component {
constructor(props) {
super(props);
this.state = {
style: {},
};
}
onFocus = () => {
const state = { ...this.state };
state.style = {
borderStyle: 'solid',
borderColor: '#e74712',
};
this.setState(state);
}
onBlur = () => {
console.log('on ONBLUR')
const state = { ...this.state };
state.style = {};
this.setState(state);
}
render = () => <TextInput style={[styles.input, this.state.style]} onFocus={() => this.onFocus()} onBlur={() => this.onBlur()} />;
}
const styles = StyleSheet.create({
input: {
color: '#000000',
fontFamily: 'MuseoSans 700 Italic',
fontSize: 22,
borderRadius: 34,
borderStyle: 'solid',
borderColor: 'transparent',
borderWidth: 5,
backgroundColor: '#ffffff',
textAlign: 'center',
width: '25%',
},
});
export default TxtInput;
I added the style into an array, have all the actual input styling done on the first property of the array and the second one the nit picking of the focus and blue.
Hope it helps
For that propose I design this simple logic in functional component (it works the same in class components with the pertinents changes), it can be apply to several <textinputs/>. Below I leave an example:
// state
const [isFocused, setIsFocused] = useState({
name: false,
email: false,
phone: false,
})
// handlers
const handleInputFocus = (textinput) => {
setIsFocused({
[textinput]: true
})
}
const handleInputBlur = (textinput) => {
setIsFocused({
[textinput]: false
})
}
// JSX
<View style={styles.form} >
<TextInput
style={isFocused.name ? [styles.inputs, { borderColor: 'blue' }] : styles.inputs}
placeholder='Name'
placeholderTextColor={darkColors.text}
textContentType='name'
keyboardType='default'
onFocus={() => handleInputFocus('name')}
onBlur={() => handleInputBlur('name')}
/>
<TextInput
style={isFocused.email ? [styles.inputs, { borderColor: 'blue' }] : styles.inputs}
placeholder='Email'
placeholderTextColor={darkColors.text}
textContentType='emailAddress'
keyboardType='email-address'
onFocus={() => handleInputFocus('email')}
onBlur={() => handleInputBlur('email')}
/>
<TextInput
style={isFocused.phone ? [styles.inputs, { borderColor: 'blue' }] : styles.inputs}
placeholder='Phone'
placeholderTextColor={darkColors.text}
keyboardType='phone-pad'
onFocus={() => handleInputFocus('phone')}
onBlur={() => handleInputBlur('phone')}
/>
</View>
<TextInput
style={{ backgroundColor: 'white', height: 40, width: 100, alignItems: 'center'
}}
theme={{ colors: { placeholder: 'white', text: 'white', primary: 'white',
underlineColor: 'transparent', background: '#003489' } }}
/>
set initial value in function component
const [warnColor, setWrnColor] = useState("grey");
set this in text input
style={[styles.brdColor, { borderColor: warnColor }]}
set this in stylesheet
brdColor: {
height: 40,
borderColor: "grey",
borderBottomWidth: 1,
marginTop: heightToDp("2%"),
width: "100%",
}
Change TextInput border color on focus, by using react hooks useState and useCallback.
const [isFocused, setIsFocused] = useState(false);
const handleFocus = useCallback(() => {
setIsFocused(!isFocused);
}, [isFocused]);
let bcOnFocus = {
borderColor: isFocused ? '#000000' : '#cccccc',
};
<ScrollView>
<View>
<TextInput
placeholder="Text Field"
style={bcOnFocus}
onFocus={handleFocus}
/>
</View>
</ScrollView>

Categories