React Native-- onPress extract id from "currentTarget" - react-native

I have created a game on React and I am trying to adapt my code to React Native. One of the things that is troubling me is how to translate these three lines, since in RN there are no DOM solutions to rely on:
handleClick(e) {
this.props.change(e.currentTarget.id);
}
What is happening here is that a stateless child is harvesting a clicked elements id (the currentTarget's) and is using it to call with it a method defined inside the parent. This kind of formulation e.currentTarget.id however does not work in RN.
Is there an eloquent way to re-write this one liner in RN?
Note: there are two questions vaguely resembling this one, here and here, however the answers look like more like patches than a structural elegant solution. If you are aware of something pls post an answer.
Edit: It seems that one cannot go around ReactNativeComponentTree.
I have that much so far but this does not work yet:
handlePress(event) {
let number = ReactNativeComponentTree.getInstanceFromNode(event.currentTarget)._currentElement.id;
this.props.change(number);
}
Second Edit: Ok maybe I should add a simplistic example of what I am trying to achieve. When I click on any of the flatlist's elements its id should be displayed on the Child's bottom . Clicking on reset will restore default state.
Code of simplistic example below:
import React, { Component } from 'react';
import { AppRegistry, FlatList, StyleSheet, Text, View, Button } from 'react-native';
import ReactNativeComponentTree from 'react-native';
export default class Parent extends Component {
constructor(props) {
super(props);
this.state = {
quotes: ["a","bnaskdkahhahskkdk","c","d","e","a","b","c","d"],
size: [true, true, true, true, true, true, true, true, true],
color: [false, false, false, false, false, false, false, false, false],
progress: "me"
};
this.change = this.change.bind(this);
this.reset = this.reset.bind(this);
}
change(number) {
this.setState({color: [true, true, true, true, true, true, true, true, true], progress: number});
}
reset() {
this.setState({color: [false, false, false, false, false, false, false, false, false],
progress: "me"
});
}
render() {
return (
<View style={styles.container}>
<Child change={this.change} reset={this.reset} quotes={this.state.quotes}
size={this.state.size} color={this.state.color}
progress={this.state.progress} />
</View>
);
}
}
class Child extends Component {
constructor(props) {
super(props);
this.handlePress = this.handlePress.bind(this);
this.handleReset = this.handleReset.bind(this);
}
/*handlePress(e) {
let number = e.currentTarget.id;
this.props.change(number);
}*/
handlePress(event) {
let number = ReactNativeComponentTree.getInstanceFromNode(event.currentTarget)._currentElement.id;
this.props.change(number);
}
handleReset() {
this.props.reset();
}
render() {
let ar = [];
for (let i=0; i<this.props.quotes.length; i++) {
let b = {key: `${i}`, id: i,
classSize: this.props.size[i] ? (i%2===0 ? styles.size : styles.oddsize) : "",
classColor: this.props.color[i] ? (i%2===0 ? styles.color : styles.oddcolor) : ""}
ar.push(b);
}
return (
<View style={styles.container}>
<Button onPress={this.handleReset} title="Reset" />
<FlatList
data={
ar
}
renderItem={({item}) => <Text onPress={this.handlePress}
style={[item.classSize, item.classColor]}> {item.id+1}
{this.props.quotes[item.id]} </Text> }
/>
<Text style={styles.size}>{this.props.progress}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: "column",
//justifyContent: "center",
alignItems: "center",
paddingTop: 22,
//backgroundColor: "purple"
},
size: {
flex: 1,
padding: 10,
fontSize: 18,
backgroundColor: "grey",
margin: 1,
height: 44,
color: 'gold',
borderColor: "white",
borderWidth: "1",
textAlign: "center"
},
oddsize: {
flex: 1,
padding: 10,
fontSize: 18,
backgroundColor: "white",
margin: 1,
height: 44,
color: 'gold',
borderColor: "white",
borderWidth: "1",
textAlign: "center"
},
color: {
flex: 1,
padding: 10,
backgroundColor: 'grey',
//borderRadius: "25%",
margin: 1,
fontSize: 18,
height: 44,
color: 'pink',
borderColor: "red",
borderWidth: "1"
},
oddcolor: {
flex: 1,
padding: 10,
backgroundColor: 'white',
//borderRadius: "25%",
margin: 1,
fontSize: 18,
height: 44,
color: 'pink',
borderColor: "red",
borderWidth: "1"
}
})
// skip this line if using Create React Native App
AppRegistry.registerComponent('AwesomeProject', () => Parent);

A better way (avoiding event callbacks creation at every render)
to get the current pressed element properties (id in this example)
is by wrapping it in a parent component, passing data
and binding all events only once (in constructor)
First declare your component wrapper:
It needs to be a class and not a stateless functional component otherwise you can't avoid the callback creation at every render
to see the difference, here's a more advanced example in action:
https://snack.expo.io/ByTEKgEsZ (example source code)
class TouchableText extends React.PureComponent {
constructor(props) {
super(props);
this.textPressed = this.textPressed.bind(this);
}
textPressed(){
this.props.onPressItem(this.props.id);
}
render() {
return (
<Text style={styles.item} onPress={this.textPressed}>
{this.props.children}
</Text>
);
}
}
If you use a const JSX object (stateless functional component), it works but it's not optimal, the event callback will be created every time the component is rendered as the arrow function is actually a shortcut to the render function
const TouchableText = props => {
const textPressed = () => {
props.onPressItem(props.id);
};
return <Text onPress={textPressed} />;
};
Then use this wrapper instead of your component as following:
class Test extends React.Component {
constructor(props) {
super(props);
//event binding in constructor for performance (happens only once)
//see facebook advice:
//https://facebook.github.io/react/docs/handling-events.html
this.handlePress = this.handlePress.bind(this);
}
handlePress(id) {
//Do what you want with the id
}
render() {
return (
<View>
<FlatList
data={ar}
renderItem={({ item }) => (
<TouchableText
id={item.id}
onPressItem={this.handlePress}
>
{`Component with id ${item.id}`}
</TouchableText>
)}
/>
</View>
);
}
}
As written in React Native Handling Events Doc, there's another possible syntax to avoid binding in the constructor (experimental though: i.e. which may or may not be supported in the future!):
If calling bind annoys you, there are two ways you can get around this. If you are using the experimental property initializer syntax, you can use property initializers to correctly bind callbacks
this means we could only write
handlePress = (id) => {
//`this` is already bound!
}
instead of
constructor(props) {
super(props);
//manually bind `this` in the constructor
this.handlePress = this.handlePress.bind(this);
}
handlePress(id) {
}
Some references:
Event handlers and Functional Stateless Components
React Binding Patterns: 5 Approaches for Handling this

After 8 hours of searching I ve found the solution on my own, thanks to the fantastic calculator tutorial of Kyle Banks.
https://kylewbanks.com/blog/react-native-tutorial-part-3-developing-a-calculator
Well basically the solution is in binding item.id along this in the assignment of the onPress event listener, like so:
renderItem={({item}) => <Text
onPress={this.handlePress.bind(this, item.id)}
style={[item.classSize, item.classColor]}>
{item.id+1}
{this.props.quotes[item.id]}
</Text> }
After doing so, the only thing you have to do is to define handlePress like so:
handlePress(e) {
this.props.change(e);
}
Where e is the item.id, on the Child and change() on the Parent:
change(number) {
this.setState({color: [true, true, true, true, true, true, true, true, true], progress: number});
}
The complete code works as intended.
import React, { Component } from 'react';
import { AppRegistry, FlatList, StyleSheet, Text, View, Button } from 'react-native';
import ReactNativeComponentTree from 'react-native';
export default class Parent extends Component {
constructor(props) {
super(props);
this.state = {
quotes: ["a","bnaskdkahhahskkdk","c","d","e","a","b","c","d"],
size: [true, true, true, true, true, true, true, true, true],
color: [false, false, false, false, false, false, false, false, false],
progress: "me"
};
this.change = this.change.bind(this);
this.reset = this.reset.bind(this);
}
change(number) {
this.setState({color: [true, true, true, true, true, true, true, true, true], progress: number});
}
reset() {
this.setState({color: [false, false, false, false, false, false, false, false, false],
progress: "me"
});
}
render() {
return (
<View style={styles.container}>
<Child change={this.change} reset={this.reset} quotes={this.state.quotes}
size={this.state.size} color={this.state.color}
progress={this.state.progress} />
</View>
);
}
}
class Child extends Component {
constructor(props) {
super(props);
this.handlePress = this.handlePress.bind(this);
this.handleReset = this.handleReset.bind(this);
}
handlePress(e) {
this.props.change(e);
}
/* handlePress(event) {
let number = ReactNativeComponentTree.getInstanceFromNode(event.currentTarget)._currentElement.id;
this.props.change(number);
} */
handleReset() {
this.props.reset();
}
render() {
let ar = [];
for (let i=0; i<this.props.quotes.length; i++) {
let b = {key: `${i}`, id: i,
classSize: this.props.size[i] ? (i%2===0 ? styles.size : styles.oddsize) : "",
classColor: this.props.color[i] ? (i%2===0 ? styles.color : styles.oddcolor) : ""}
ar.push(b);
}
return (
<View style={styles.container}>
<Button onPress={this.handleReset} title="Reset" />
<FlatList
data={
ar
}
renderItem={({item}) => <Text onPress={this.handlePress.bind(this, item.id)}
style={[item.classSize, item.classColor]}> {item.id+1}
{this.props.quotes[item.id]} </Text> }
/>
<Text style={styles.size}>{this.props.progress}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: "column",
//justifyContent: "center",
alignItems: "center",
paddingTop: 22,
//backgroundColor: "purple"
},
size: {
flex: 1,
padding: 10,
fontSize: 18,
backgroundColor: "grey",
margin: 1,
height: 44,
color: 'gold',
borderColor: "white",
borderWidth: "1",
textAlign: "center"
},
oddsize: {
flex: 1,
padding: 10,
fontSize: 18,
backgroundColor: "white",
margin: 1,
height: 44,
color: 'gold',
borderColor: "white",
borderWidth: "1",
textAlign: "center"
},
color: {
flex: 1,
padding: 10,
backgroundColor: 'grey',
//borderRadius: "25%",
margin: 1,
fontSize: 18,
height: 44,
color: 'pink',
borderColor: "red",
borderWidth: "1"
},
oddcolor: {
flex: 1,
padding: 10,
backgroundColor: 'white',
//borderRadius: "25%",
margin: 1,
fontSize: 18,
height: 44,
color: 'pink',
borderColor: "red",
borderWidth: "1"
}
})
// skip this line if using Create React Native App
AppRegistry.registerComponent('AwesomeProject', () => Parent);

Related

Adding data to FlatList clears the whole list

I am new to react-native. I have a flatlist, that takes data from "state". This works. Then, I added a new function in order to add new data to the flatlist (additionally to the existing data).
As soon as I click on the "delete" button ( don't mind the name) the data of the flatlist is being deleted completely. I want the output to be like
Object 1
Object 2
Object 3 (has been added after button click)
What am I doing wrong? Can you please explain me the reason?
EDIT: I get this warning, but no error.
VirtualizedList: missing keys for items, make sure to specify a key or
id property on each item or provide a custom keyExtractor.,
import React, { Component } from "react";
import { Text, View, StyleSheet, Button, FlatList } from "react-native";
class App extends Component {
state = {
counter: 0,
initialElements: [
{ id: "0", text: "Object 1" },
{ id: "1", text: "Object 2" },
],
};
render() {
const currentCounter = this.state.counter;
const exampleState = this.state.initialElements;
const addElement = () => {
let newArray = [...exampleState, { id: "2", text: "Object 3" }];
this.setState({
initialElements: [newArray],
});
};
return (
<View style={styles.container}>
<View style={styles.counter}>
<Text style={styles.textStyle}>{currentCounter}</Text>
</View>
<View style={styles.deleteButton}>
<Button
title="Hello"
onPress={() =>
this.setState({
counter: currentCounter + 1,
})
}
></Button>
<View style={styles.space}></View>
<Button title="Delete" onPress={addElement}></Button>{" "}
{/* as soon as I click here, the content of the list is being deleted */}
</View>
<FlatList
style={styles.listStyle}
key={(item) => item.id}
data={exampleState}
renderItem={(item) => <Text>{item.item.text}</Text>}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: "#fff",
justifyContent: "center",
flex: 1,
flexDirection: "column",
alignItems: "center",
},
counter: {
flexDirection: "row",
justifyContent: "center",
},
deleteButton: {
flexDirection: "row",
margin: 5,
marginTop: 100,
},
space: {
width: 5,
height: 5,
},
textStyle: {
margin: 80,
fontSize: 100,
},
listStyle: {
flexDirection: "column",
},
item: {
padding: 10,
fontSize: 18,
height: 44,
},
});
export default App;
I fixed it by changing the addElement to function to:
const addElement = () => {
var newArray = [...exampleState, {id : 2, text : "Object 3"}];
this.setState( {
initialElements: newArray
});
}
So, basically I changed
initialElements: [newArray]
to
initialElements: newArray
because newArray is already an Array, so no need to wrap it again.

ComponentWillUnmount is not called when working with createMaterialTopTabNavigator()

I have created 5 tabs using createMaterialTopTabNavigator and implemented search bar on each of tabs. However, if I am switching between tabs, components will persist the data and did not refresh. Our componentWillUnmount() method is not invoked.
import React, { Component } from "react";
import {
View,
Text,
StyleSheet,
SafeAreaView,
FlatList,
TouchableOpacity,
TextInput
} from "react-native";
import SegmentedControlTab from "react-native-segmented-control-tab";
let context;
import MachineInformationCard from './MachineInformationCard';
export default class AllMachine extends Component {
constructor(props) {
super(props);
context = this;
this.state = {
selectedIndex: 0,
machineData: [],
tempMachineData: []
}
}
componentDidMount = () => {
const fetchData = [{
'Status': 'ON',
'MachineName': '1',
'OperatorName': 'Abhishek',
'Target': 500,
'Achieved': 200,
'Health': '60%',
'Shift': 1,
'Date': '29-10-2019'
},
{
'Status': 'Maintenance',
'MachineName': '12',
'OperatorName': 'Vicky',
'Target': 600,
'Achieved': 100,
'Health': '20%',
'Shift': 2,
'Date': '30-10-2019'
},
{
'Status': 'OFF',
'MachineName': '3',
'OperatorName': 'Prasad',
'Target': 700,
'Achieved': 700,
'Health': '80%',
'Shift': 3,
'Date': '28-10-2019'
},
{
'Status': 'OFF',
'MachineName': '34',
'OperatorName': 'Prasad',
'Target': 700,
'Achieved': 700,
'Health': '80%',
'Shift': 3,
'Date': '28-10-2019'
}];
this.setState({
machineData: fetchData,
tempMachineData: fetchData,
})
}
getSearchValue = (data) => {
const filteredMachineNames = this.state.tempMachineData.filter(
macName => {
let machineNameLowercase = macName.MachineName.toLowerCase()
let searchTermLowercase = data.toLowerCase()
return machineNameLowercase.indexOf(searchTermLowercase) > -1;
}
)
this.setState({
machineData: filteredMachineNames
})
}
_renderItem({ item, index }) {
return (
<TouchableOpacity
onPress={() => context.props.navigation.navigate('MachineProfile')}>
<MachineInformationCard {...item} />
</TouchableOpacity>
)
}
render() {
return (
<SafeAreaView style={styles.container}>
<View style={{
backgroundColor: '#E1E1E1', borderRadius: 30,
flexDirection: 'row', marginTop: 5
}}>
<TextInput style={{
color: 'black', fontSize: 17, height: 40, padding: 10
}}
placeholder={'Search Machines'}
placeholderTextColor={'grey'}
onChangeText={(value) => this.getSearchValue(value)} />
</View>
<FlatList
data={this.state.machineData}
renderItem={this._renderItem}
keyExtractor={({ id }, index) => id} />
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
CardItem: {
flex: 1,
flexDirection: 'row',
height: 120,
backgroundColor: '#e7e7e7',
borderRadius: 5,
overflow: 'hidden',
margin: 5,
shadowColor: 'black',
shadowOpacity: 0.8,
elevation: 8
}
});
[[screen 1 with search][when I switch to ON tab and come back to All machine, I want All machine to be display entire list instead of filtered list]][same]
components will not be unmounted in a tabbed layout. you must use navigation events to determine if the screen comes into or goes out of focus - documentation
example -
const willFocusSubscription = this.props.navigation.addListener(
'willFocus',
payload => {
// callback when component comes into view
console.debug('willFocus', payload);
}
);
// Remove the listener when you are done (usually in componentWillUnmount)
// willFocusSubscription.remove();
events -
willFocus - the screen will focus
didFocus - the screen focused (if there was a transition, the transition completed)
willBlur - the screen will be unfocused
didBlur - the screen unfocused (if there was a transition, the transition completed)

How to select a single radio button at a time in react native flatlist

I am using react-native-radio-button to implement a flatlist with radio button as shown in the image.
[![enter image description here][1]][1]
But i am unable to select a single radio button at a time in flatlist.
Here is my code
import React from 'react';
import { StyleSheet, View, Text, FlatList, TouchableOpacity, Image, LayoutAnimation, UIManager, Platform } from 'react-native';
import RadioButton from 'react-native-radio-button';
import PHARMACY from './api/mockPharmacyList';
import { Colors, Fonts } from '../../styles';
interface IState {
selected: number;
selectedIndex: number;
data: any;
expanded: boolean;
checked: boolean;
}
export class TransferToPharmacy extends React.Component<{}, IState> {
constructor(props) {
super(props);
this.state = {
data: PHARMACY,
selected: 0,
selectedIndex: 0,
expanded: true,
checked: false,
};
this.onPress = this.onPress.bind(this);
this.renderItem = this.renderItem.bind(this);
this.renderSeparator = this.renderSeparator.bind(this);
this._keyExtractor = this._keyExtractor.bind(this);
this.changeLayout = this.changeLayout.bind(this);
this.onCheck = this.onCheck.bind(this);
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
}
changeLayout = () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
this.setState({ expanded: false });
}
getInitialState() {
return {
value: 0,
};
}
onPress(value) {
this.setState({ selected: value });
}
_keyExtractor = (item, index) => item.id;
onCheck = () => {
const { checked } = this.state;
if (checked === true) {
this.setState({ checked: false });
} else {
this.setState({ checked: true });
}
}
renderItem(data) {
const { item } = data;
return (
<View style={styles.itemBlock}>
<TouchableOpacity >
<View style={styles.arrowIconStyle}>
<Text style={styles.pharmacyText}>{item.name}</Text>
<RadioButton
innerColor={Colors.darkBlue}
outerColor={Colors.lightGray}
animation={'bounceIn'}
isSelected={this.state.selectedIndex === 0}
onPress={this.onPress}
/>
</View>
</TouchableOpacity>
<Text>{item.key1}</Text>
<View style={{ height: this.state.expanded === true ? 90 : 0, overflow: 'hidden' }}>
<View style={{ flexDirection: 'row', marginTop: 5, padding: 10 }}>
<Image style={[styles.itemImage, { width: 15, height: 15, margin: 1 }]} source={require('./images/map_pic_icon.png')} />
<Text style={styles.itemText}>{item.location}</Text>
</View>
<View style={{ flexDirection: 'row', marginTop: 5, padding: 10 }}>
<Image style={[styles.itemImage, { width: 15, height: 15, margin: 1 }]} source={require('./images/phone_icon.png')} />
<Text style={styles.itemText}>{item.phone}</Text>
</View>
</View>
</View>
);
}
renderSeparator() {
return <View style={styles.separator} />;
}
render() {
return (
<View style={styles.container} >
<Text style={styles.textStyle}>Transfer to Pharmacy</Text>
<View style={styles.childViewStyle}>
<FlatList
keyExtractor={this._keyExtractor}
data={this.state.data}
renderItem={this.renderItem}
ItemSeparatorComponent={this.renderSeparator}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
textStyle: {
fontSize: 18,
color: Colors.black,
marginTop: 30,
marginLeft: 20
},
childViewStyle: {
margin: 20,
},
itemBlock: {
paddingVertical: 15,
},
itemImage: {
width: 50,
height: 50,
borderRadius: 25,
margin: 10
},
itemText: {
fontSize: 16,
justifyContent: 'center',
color: Colors.darkySkyBlue
},
itemName: {
fontSize: 20,
color: Colors.black,
},
separator: {
borderRadius: 4,
borderWidth: 1,
borderColor: Colors.lightGray,
},
pharmacyText: {
fontSize: 16,
fontFamily: Fonts.bold,
color: Colors.black
},
radioStyle: {
marginTop: 10,
marginRight: 50,
justifyContent: 'space-between'
},
arrowIconStyle: {
flexDirection: 'row',
justifyContent: 'space-between',
padding: 10
// flex: 1
}
});
with this code i am not able to select the radio button
Please let me know where it is going wrong as i am unable to select radio button in the flatlist separately.
i have changed onPress() like this
onPress( index) {
this.setState({ selected: index, });
}
and in renderItem method i have changed it like this
const { item, index } = data;
<RadioButton
innerColor={Colors.darkBlue}
outerColor={Colors.lightGray}
animation={'bounceIn'}
isSelected={this.state.selected === index}
onPress={() => this.onPress(index)}
/>
Now the output is like this
Now the first radio button is selected but when i tried to select other radio buttons they are not getting selected.
Here is my mock data file which is used for Flatlist.
export const PHARMACY = [
{
key: 0,
name: 'Plaza Pharmacy',
type: 'Urgent Care Center - 4.01 mi',
location: '3417 Gaston Avenue, Suite 195\n Dallas, TX 75202',
phone: '469.764.7100',
},
{
key: 1,
name: 'Courtyard Pharmacy',
type: 'Urgent Care Center - 4.09 mi',
location: '5236 West University Drive, Suite 1900\n MCKINNEY, TX 75071',
phone: '969.264.7190',
},
{
key: 2,
name: 'Outptaient Pharmacy at the Cancer Center',
type: 'Urgent Care Center - 4.66 mi',
location: '3800 Gaylord Parkway, Ste 110\n FRISCO, TX 75034',
phone: '890.664.8130',
},
{
key: 3,
name: 'Carrolton Pharmacy',
type: 'Urgent Care Center - 4.08 mi',
location: '5236 West University Drive, Suite 1900\n MCKINNEY, TX 75071',
phone: '969.264.7190',
},
{
key: 4,
name: 'Garland Pharmacy',
type: 'Urgent Care Center - 22.11 mi',
location: '3417 Gaston Avenue, Suite 195\n Dallas, TX 75202',
phone: '469.764.7100',
},
];
I am exporting this const PHARMACY and importing it in my class and set this to a state variable "data".
you can do something like this in renderItem
renderItem = ({item, index}) => {
.....
<RadioButton
innerColor={Colors.darkBlue}
outerColor={Colors.lightGray}
animation={'bounceIn'}
isSelected={this.state.selectedIndex === index}
onPress={() => {this.onPress(index)}}
/>
}
and replace onPress with
onPress = (index) => {
this.setState({ selectedIndex: index });
}
and update FlatList with extraData props as FlatList needs to be re-rendered as
<FlatList
keyExtractor={this._keyExtractor}
data={this.state.data}
renderItem={this.renderItem}
ItemSeparatorComponent={this.renderSeparator}
extraData={this.state}
/>
You can refer in snack here
First, you need to get the index of the current item like so:
const { item, index } = data;
After you get the index of the current item you can then check if the current radio button is selected or not and for changing the radio button you need to pass the value of the current index to the onPress function.
<RadioButton
innerColor={Colors.darkBlue}
outerColor={Colors.lightGray}
animation={'bounceIn'}
isSelected={this.state.selected === index}
onPress={() => this.onPress(index)}
/>

Created a custom button component but I can't get it to appear disabled

I am creating a custom button on react native and trying to have it appear as disabled (by lowering the opacity) if the credentials are bad or upon loading.
I have the component working correctly but it won't lower the opacity
I have played around with the values but nothing seems to take, I'm new to this so maybe I'm missing something obvious.
This is my button
import React, { Component } from 'react';
import { StyleSheet, Text, TouchableOpacity } from "react-native";
import colors from "../config/colors";
export default class Button extends React.Component {
constructor(props){
super(props);
this.state = {
label: "",
disabled: true
}
}
render (){
const { disabled, label, onPress } = this.props;
const containerStyle = [
styles.container,
disabled ? styles.containerDisabled : styles.containerEnabled
];
return (
<TouchableOpacity
style={styles.container}
onPress={onPress}
disabled={disabled}
>
<Text style={styles.text}>{label}</Text>
</TouchableOpacity>
);
}
}
const styles = StyleSheet.create({
container: {
width: "100%",
alignItems: "center",
justifyContent: "center",
backgroundColor: colors.BLUE,
marginBottom: 12,
paddingVertical: 12,
borderRadius: 4,
borderWidth: StyleSheet.hairlineWidth,
borderColor: "rgba(255, 255, 255, 0.7)",
},
containerEnabled: {
opacity: 1
},
containerDisabled: {
opacity: 0.3
},
text: {
color: colors.WHITE,
textAlign: "center",
height: 20
}
});
It appears with opacity:1 even though I meant for it to be 0.3 from the beginning.
On my app.js I call it by doing
constructor(props){
super(props);
this.state = {
email: "",
password: "",
emailTouched: false,
passwordTouched: false
}
...
render() {
const {
email,
password,
emailTouched,
passwordTouched
} = this.state;
...
<Button
label={strings.LOGIN}
onPress={this.handleLoginPress}
disabled={!email || !password}
/>
In your render method - you don't have any prop of disabled, rather use const {disabled} = this.state. that should work

Handling focus in react native with multiple input

I am looking to only toggle the focus state on one of the inputs instead of both when onFocus is fired.
With both inputs having the 'onFocus' and 'onBlur' events I was expecting the state to update on only the current element that is focussed.
Should I be using refs instead of state for this sort of event handling?
class SignInScreen extends Component {
state = {
isFocused: false,
style: {
inputContainer: styles.input
}
}
onFocus = () => {
if(!this.state.isFocused) {
this.setState({
isFocused: true,
style: {
inputContainer: styles.inputDifferent
}
})
}
}
onBlur = () => {
if(this.state.isFocused) {
this.setState({
isFocused: false,
style: {
inputContainer: styles.input
}
})
}
}
render() {
return (
<Input
containerStyle={[styles.input, this.state.style.inputContainer]}
onFocus={this.onFocus}
onBlur={this.onBlur}
/>
<Input
containerStyle={[styles.input, this.state.style.inputContainer]}
onFocus={this.onFocus}
onBlur={this.onBlur}
/>
)
}
}
const styles = StyleSheet.create({
input: {
borderWidth: 1,
borderColor: 'white,
marginBottom: 16,
maxWidth: '90%',
marginLeft: 'auto',
marginRight: 'auto'
},
inputDifferent: {
backgroundColor: 'gray',
}
});
Modify your code Like This:
Edited:
class SignInScreen extends Component {
state = {
isFocused: false,
firstLoad: true
}
onFocus = () => {
this.setState({
isFocused: !this.state.isFocused,
firstLoad: false
})
}
render() {
const {isFocused} = this.state
return (
<View>
<Input
containerStyle={isFocused || firstLoad ? styles.input : styles.inputDifferent}
onFocus={this.onFocus}
/>
<Input
containerStyle={!isFocused || firstLoad ? styles.input : styles.inputDifferent}
onFocus={this.onFocus}
/>
</View>
)
}
}
const styles = StyleSheet.create({
input: {
borderWidth: 1,
borderColor: 'white',
marginBottom: 16,
maxWidth: '90%',
marginLeft: 'auto',
marginRight: 'auto'
},
inputDifferent: {
backgroundColor: 'gray',
}
});
its unnecessary to include styles on state. it is better to only use this.state.isFocus to make code simplier. just used this to make a condition on what style the inputs will be using.
you can also disregard onBlur. because all we need on the conditions is isFocus.
the key factor here is the exclamation symbol that will make the state toggle.
Edited: firstLoad state added to make the style of both inputs the same on load. that will be used on the condition inside input styles.
note: it can only work with two inputs