Focus style for TextInput in react-native - javascript

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>

Related

Calling useNavigation hook from TouchableOpacity inside a DrawerNavigator from react-navigation pkg throwing error, full source on question body

I'm learning react and react-native last 4 days because need build an mobile app and having the following problem when coding a react-native app:
After some research I found a way to embed a TouchableOpacity inside the top bar of DrawerNavigator, just after the hamburger button and it works giving me an alert.
The problem comes when I try to replace the alert with a call to useNavigation().navigate('Login'); from inside it onPress attribute, it throw the error below.
I want mirror the behavior of menu item on the TouchableOpacity doing the navigation.
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem., js engine: hermes
info Reloading app...
The full source follow as:
import * as React from 'react';
import { Button, Image, StyleSheet, Text, TouchableOpacity, View, Alert } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import {
createDrawerNavigator,
DrawerContentScrollView,
DrawerItemList,
} from '#react-navigation/drawer';
// https://reactnavigation.org/docs/use-navigation/
import { useNavigation } from '#react-navigation/native';
const getScreenCtx = content => {
return (<View style={stl.boxCtx}>{content}</View>);
}
const Home = ({ navigation }) => {
return ( getScreenCtx(<Text style={stl.boxDescr}>Home</Text>) );
}
const Login = ({ navigation }) => {
return ( getScreenCtx(<Text style={stl.boxDescr}>Login</Text>) );
}
const Logout = ({ navigation }) => {
return ( getScreenCtx(<Text style={stl.boxDescr}>Logout</Text>) );
}
const navToLogin = () => {
useNavigation().navigate('Login');
}
const NestedTabBar = props => {
return (
<>
<TouchableOpacity
style={stl.itemNav}
onPress={
() => {
//Alert.alert('NavigateToLogin');
navToLogin();
}
} >
<Text>[NavigateToLogin]</Text>
</TouchableOpacity>
</>
);
}
const ContentTopWideHamburgBar = props => {
return <NestedTabBar />;// <Text style={stl.hamburgBar}>ContentTopHamburgBar</Text>
}
const ContentColapsibleSideBarMenuHeader = props => {
return <Text style={stl.sideMenuHeader}>SideBarMenuHeader</Text>
}
const ContentColapsibleSideBarMenuFooter = props => {
return <Text style={stl.sideMenuFooter}>SideBarMenuFooter</Text>
}
const ContentColapsibleSideBarMenu = props => {
return (
<View style={stl.sideBarMenu}>
<DrawerContentScrollView {...props}>
<ContentColapsibleSideBarMenuHeader/>
<DrawerItemList {...props} />
</DrawerContentScrollView>
<ContentColapsibleSideBarMenuFooter/>
</View>
);
}
const Drawer = createDrawerNavigator();
const ContentItensNavigationRouteMap = () => {
return (
<>
<Drawer.Screen component={Home} name='Home' />
<Drawer.Screen component={Login} name='Login' />
<Drawer.Screen component={Logout} name='Logout' />
</>
);
}
const DrawerNavigator = () => {
return (
<Drawer.Navigator
screenOptions={
{
headerStyle: { backgroundColor: 'magenta', },
headerTintColor: 'white', // hamburg color
headerTitleStyle: { fontWeight: 'bold', },
headerTitle: (props) => <ContentTopWideHamburgBar {...props} />
}
}
contentOptions={
{
activeTintColor: 'red',
activeBackgroundColor: 'red',
inactiveTintColor: 'red',
inactiveBackgroundColor: 'red',
}
}
drawerContent={props => <ContentColapsibleSideBarMenu {...props} />} >
{ContentItensNavigationRouteMap()}
</Drawer.Navigator>
);
};
export default function ShellNavigator() {
return (
<NavigationContainer>
<DrawerNavigator />
</NavigationContainer>
);
}
const stl = StyleSheet.create({
boxDescr: {
fontSize: 30,
margin: 10,
padding:10,
backgroundColor: 'lightblue',
color: 'red',
},
boxCtx:{
display: 'flex',
flex: 1,
fontSize: 30,
margin: 0,
backgroundColor: 'yellow',
},
hamburgBar: {
fontSize: 20,
margin: 10,
backgroundColor: 'pink',
},
sideMenuHeader: {
fontSize: 20,
margin: 10,
backgroundColor: 'blueviolet',
},
sideMenuFooter: {
fontSize: 20,
margin: 10,
backgroundColor: 'purple',
},
sideBarMenu: {
flex: 1,
fontSize: 20,
margin: 10,
backgroundColor: 'greenyellow',
},
itemNav: {
fontSize: 40,
padding: 10,
backgroundColor: 'red',
},
});
Here navToLogin is a stand alone function, not in a Functional component. Hooks need to be in a functional component to use. The bellow code should work which navToLogin move the function inside a functional component.
const NestedTabBar = props => {
const navigation = useNavigation();
const navToLogin = () => {
navigation.navigate('Login');
}
return (
<>
<TouchableOpacity
style={stl.itemNav}
onPress={
() => {
//Alert.alert('NavigateToLogin');
navToLogin();
}
} >
<Text>[NavigateToLogin]</Text>
</TouchableOpacity>
</>
);
}

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

Component not rendering in react-native

Though I am experienced in React I am very new to react-native. I have tried several answers posted in SO for the same issue but none of them helping me fix the issue.
I have App component and the App component calls Account component. The Account component renders input fields but the input fields are not displayed in UI but If I add only Text in App component like <Text>Hello</Text> it is showing in UI but my custom Account component is not showing in the UI. I am not getting an idea what I am doing wrong.
PFB component details
App.js
import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Account from './components/Form/components/Account';
export default class App extends Component {
render() {
return (
<View style={styles.container}>
<Account />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Account.js
import React, { Component } from "react";
import { StyleSheet, Text, View, Button, TextInput, ScrollView } from 'react-native';
export default class Account extends Component {
constructor(props){
super(props);
this.state = {
cardNo: "",
amount: 0
}
}
handleCardNo = no => {
this.setState({
cardNo: no
});
}
handleAmount = amount => {
this.setState({
amount
});
}
handleSend = event => {
const { cardNo, amount } = this.state;
this.setState({
loading: true
});
const regex = /^[0-9]{1,10}$/;
if(cardNo == null){
}else if (!regex.test(cardNo)){
//card number must be a number
}else if(!regex.test(amount)){
//amount must be a number
}else{
// const obj = {};
// obj.cardNo = cardNo;
// obj.amount = amount;
// const url = "http://localhost:8080/apple"
// axios.post(url, obj)
// .then(response => return response.json())
// .then(data => this.setState({
// success: data,
// error: "",
// loading: false
// }))
// .catch(err => {
// this.setState({
// error: err,
// success: "",
// loading: false
// })
// })
//values are valid
}
}
render(){
const { cardNo, amount } = this.state;
return(
<View>
<TextInput label='Card Number' style={{height: 40, flex:1, borderColor: '#333', borderWidth: 1}} value={cardNo} onChangeText={this.handleCardNo} />
<TextInput label='Amount' style={{height: 40, flex:1, borderColor: '#333', borderWidth: 1}} value={amount} onChangeText={this.handleAmount} />
<Button title='Send' onPress={this.handleSend}/>
</View>
)
}
}
Your code has some styling issues. Try changing you render function with this
render() {
const { cardNo, amount } = this.state
return (
<View style={{ alignSelf: 'stretch' }}>
<TextInput
placeholder="Card Number"
style={{ height: 40, borderColor: '#333', borderWidth: 1 }}
value={cardNo}
onChangeText={this.handleCardNo}
/>
<TextInput
placeholder="Amount"
style={{ height: 40, borderColor: '#333', borderWidth: 1 }}
value={amount}
onChangeText={this.handleAmount}
/>
<Button title="Send" onPress={this.handleSend} />
</View>
)
}

How can I switch to the second input when first will be done in React Native

I am creating a Taxi Application just like Uber where user enters two locations in given inputs (Pickup and Dropoff), I done with the PickUp when User enters a location it shows the googleautocomplete listview for location and can select the pickup location, but I am stuch with the Dropoff when I write something in the second input it changes the first too or when I click the clear icon it clears text from both inputs. So what do I have to do to solve this problem kindly help please Thanks.
AutoCompeleteInputs.js
import React from 'react';
import { TextInput, View, StyleSheet, Animated, TouchableOpacity } from "react-native";
import AutoCompleteListView from "./AutoCompleteListView";
import Events from "react-native-simple-events";
import debounce from '../utils/debounce'
import fetch from 'react-native-cancelable-fetch';
import MaterialIcons from "react-native-vector-icons/MaterialIcons";
const AUTOCOMPLETE_URL = "https://maps.googleapis.com/maps/api/place/autocomplete/json";
const REVRSE_GEO_CODE_URL = "https://maps.googleapis.com/maps/api/geocode/json";
export default class AutoCompleteInput extends React.Component {
static defaultProps = {
language: 'en'
};
constructor(props) {
super(props);
}
componentWillUnmount() {
this._abortRequest();
}
state = {
predictions: [],
loading: false,
inFocus: false,
};
_abortRequest = () => {
fetch.abort(this);
};
fetchAddressForLocation = (location) => {
this.setState({ loading: true, predictions: [] });
let { latitude, longitude } = location;
fetch(`${REVRSE_GEO_CODE_URL}?key=${API_KEY}&latlng=${latitude},${longitude}`, null, this)
.then(res => res.json())
.then(data => {
this.setState({ loading: false });
let { results } = data;
if (results.length > 0) {
let { formatted_address } = results[0];
this.setState({ text: formatted_address });
}
});
}
_request = (text) => {
this._abortRequest();
if (text.length >= 3) {
fetch(`${AUTOCOMPLETE_URL}?input=${encodeURIComponent(text)}&key=${API_KEY}&language=${this.props.language}`, null, this)
.then(res => res.json())
.then(data => {
let { predictions } = data;
this.setState({ predictions });
});
} else {
this.setState({ predictions: [] });
}
}
_onChangeText = (text) => {
this._request(text);
this.setState({ text });
}
_onFocus = () => {
this._abortRequest();
this.setState({ loading: false, inFocus: true });
Events.trigger('InputFocus');
}
_onBlur = () => {
this.setState({ inFocus: false });
Events.trigger('InputBlur');
}
blur = () => {
this._input.blur();
}
_onPressClear = () => {
this.setState({ text: '', predictions: [] });
}
_getClearButton = () => {
return this.state.inFocus ?
(<TouchableOpacity style={styles.btn} onPress={this._onPressClear}>
<MaterialIcons name={'clear'} size={20} />
</TouchableOpacity>) : null;
}
getAddress = () => {
return this.state.loading ? '' : this.state.text;
}
render() {
return (
<Animated.View style={this.props.style}>
<View style={{ flexDirection: 'column' }}>
<View
style={styles.textInputPickupContainer}
elevation={5}>
<TextInput
ref={input => this._input = input}
value={this.state.loading ? 'Loading...' : this.state.text}
style={styles.textInput}
underlineColorAndroid={'transparent'}
placeholder={'Pickup'}
onFocus={this._onFocus}
onBlur={this._onBlur}
onChangeText={this._onChangeText}
outlineProvider='bounds'
autoCorrect={false}
spellCheck={false} />
{this._getClearButton()}
</View>
<View
style={styles.textInputDropoffContainer}
elevation={5}>
<TextInput
ref={input => this._input = input}
value={this.state.loading ? 'Loading...' : this.state.dropOff}
style={styles.textInput}
underlineColorAndroid={'transparent'}
placeholder={'Dropoff'}
onFocus={this._onFocus}
onBlur={this._onBlur}
onChangeText={(text) => this.setState({dropOff: text})}
outlineProvider='bounds'
autoCorrect={false}
spellCheck={false} />
{this._getClearButton()}
</View>
</View>
<View style={styles.listViewContainer}>
<AutoCompleteListView
predictions={this.state.predictions} />
</View>
</Animated.View>
);
}
}
const styles = StyleSheet.create({
textInputPickupContainer: {
flexDirection: 'row',
height: 40,
zIndex: 10,
paddingLeft: 10,
borderRadius: 5,
backgroundColor: 'white',
shadowOffset: {
width: 0,
height: 2
},
shadowRadius: 2,
alignItems: 'center'
},
textInputDropoffContainer: {
flexDirection: 'row',
height: 40,
zIndex: 10,
paddingLeft: 10,
marginTop: 10,
borderRadius: 5,
backgroundColor: 'white',
shadowOffset: {
width: 0,
height: 2
},
shadowRadius: 2,
alignItems: 'center'
},
textInput: {
flex: 1,
fontSize: 17,
color: '#404752'
},
btn: {
width: 30,
height: 30,
padding: 5,
justifyContent: 'center',
alignItems: 'center'
},
listViewContainer: {
paddingLeft: 3,
paddingRight: 3,
paddingBottom: 3
},
});
SearchRoute.js(Main)
return (
<View style={styles.container}>
<MapView
ref={(mapView) => this._map = mapView}
style={styles.mapView}
region={this.state.region}
showsMyLocationButton={true}
showsUserLocation={false}
onPress={({ nativeEvent }) => this._setRegion(nativeEvent.coordinate)}
onRegionChange={this._onMapRegionChange}
onRegionChangeComplete={this._onMapRegionChangeComplete}
/>
<Entypo name={'location-pin'} size={30}
color={this.props.markerColor}
style={{ backgroundColor: 'transparent' }} />
<View style={styles.fullWidthContainer}>
<AutoCompleteInput
ref={input => this._input = input}
apiKey={API_KEY}
style={[styles.input, { transform: [{ scale: inputScale }] }]}
debounceDuration={this.props.debounceDuration}
/>
</View>
The error is that you are assigning the same value to both TextInputs, so if you change one, the other TextInput will change too to the same value.
On your AutoCompeleteInputs.js change dropOff TextInput's value to
<TextInput
...
value={this.state.loading ? 'Loading...' : this.state.dropOff}
...
/>
So when you change dropOff value, the pickUp point will be the same you entered before, and if you clear one the other will not change.
Also change dropOff onChangeText={this._onChangeText} to onChangeText={(text) => this.setState({dropOff: text}) to ensure that you are storing the correct value.

Change button style on press in React Native

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

Categories