How to use ref in React Navigation header - javascript

I'm adding a search bar with button (i.e. search icon) in navigationOptions of React navigation. Now I want to send the typed text with navigate function. I don't know how to pass reference of TextInput to get its native text. Following is the code I've done so far.
static navigationOptions = ({ navigation }) => (
headerStyle: {
...
},
headerTitle: (
<View style={{ width: '100%', height: 75, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', borderRadius: 50, backgroundColor: 'white' }}>
<TextInput style={{ width: '85%', padding: 15 }}
onSubmitEditing={
(e) => (e.nativeEvent.text.length > 2) && navigation.navigate('Brands', {text: e.nativeEvent.text})
} />
<TouchableOpacity style={{ width: '15%', padding: 15 }}
onPress={() => navigation.navigate('Brands'/*, {text: HERE I WANT TO GET TEXT FROM REFERENCE}*/)}>
<Icon type='FontAwesome' name='search'
style={{ color: 'red', 20, textAlign: 'center' }} />
</TouchableOpacity>
</View>
)
)};
But as navigationOptions is static, I can't use this keyword inside it. Also I can't use state inside navigationOptions as well. So how could I use ref to get input text on button press.
EDIT
I want to get TextInput value from ref
<TextInput ref={(ref) => { this.input = ref; }} />
// OR
<TextInput ref='input' />
So that I can get its value from
this.input._lastNativeText
// OR
this.refs['input']._lastNativeText
But I cant use this keyword or state in navigationOptions because its static. How can I access the value of TextInput on button click?

send callback to navigationOptions through naviagtion prop .
set callback value in componentDidMount
componentDidMount=()=> {
this.props.navigation.setParams({
onSubmitEditing: this.onSubmitEditing,
});
}
onSubmitEditing=(event)=>{
console.log(event);
//this is avaialbel here
}
Get on SubmitEdit function from naviagttion params
static navigationOptions = ({ navigation = this.props.navigation }) => {
const onSubmitEditing = navigation.state.params.onSubmitEditing;
return {
headerTitle: (
<View style={{ width: '100%', height: 75, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', borderRadius: 50, backgroundColor: 'white' }}>
<TextInput style={{ width: '85%', padding: 15 }}
onSubmitEditing={onSubmitEditing} />
<TouchableOpacity style={{ width: '15%', padding: 15 }}
onPress={() => navigation.navigate('Brands'/*, {text: HERE I WANT TO GET TEXT FROM REFERENCE}*/)}>
<Icon type='FontAwesome' name='search'
style={{ color: 'red', size: 20, textAlign: 'center' }} />
</TouchableOpacity>
</View>
)
}
};

Just in case anybody needs this
componentDidMount(){
---
this.props.navigation.setParams({
setMenuRef: this.setMenuRef.bind(this),
});
---
}
_menu = null;
setMenuRef = ref => {
this._menu = ref;
};
onSortClick() {
this._menu.show();
}
static navigationOptions = ({navigation}){
headerRight:(
---
ref={navigation.state.params && navigation.state.params.setMenuRef}
---
)
}

Related

How can I store the value of TextInput to local storage and get them when the app starts in react native?

I'm making a to-do list app. I need to store the input value locally or somewhere so that I can show them in the app even if the app is opened after closing once. Now when I'm closing the app and restarting all the previous values is being vanished. But I want to keep the previous data and If new data is given that will also be stored if I don't delete that manually. How can I solve this problem? I'm using expo.
Here is my code:
import React, { useState } from 'react';
import { View, Text, StyleSheet, Button, FlatList, TouchableOpacity, TextInput, ScrollView } from 'react-native';
import { MaterialIcons } from '#expo/vector-icons'
import Line from './Line';
function App() {
//storing data in array
const [initialElements, changeEl] = useState([
]);
const [value, setValue] = useState('')
const [idx, incr] = useState(1);
const [exampleState, setExampleState] = useState(initialElements);
//add button
const addElement = () => {
if (value != '') {
var newArray = [...initialElements, { id: idx, title: value }];
incr(idx + 1);
setExampleState(newArray);
changeEl(newArray);
}
}
//delete button
const delElement = (id) => {
let newArray = initialElements.filter(function (item) {
return item.id !== id
})
setExampleState(newArray);
changeEl(newArray);
}
//showing item
const Item = ({ title, id }) => (
<View style={{ flex: 1, flexDirection: 'row', alignItems: 'center' }}>
<View style={{ borderWidth: 2, margin: 5, flex: 1, padding: 10, borderRadius: 10 }}>
<TouchableOpacity onPress={() => { alert(title) }}>
<Text >{title}</Text>
</TouchableOpacity>
</View>
<TouchableOpacity onPress={() => delElement(id)}>
<MaterialIcons name='delete' size={24} />
</TouchableOpacity>
</View>
);
//calling item for render
const renderItem = ({ item }) => {
<Item title={item.title} id={item.id} />
}
return (
<View style={styles.container}>
<View style={{ alignItems: 'center', marginBottom: 10 }}>
<Text style={{ fontSize: 20, fontWeight: '500' }}> To <Text style={{ color: 'skyblue', fontWeight: '900' }}>Do</Text> List</Text>
<Line />
</View>
<View>
<TextInput onChangeText={text => setValue(text)} placeholder="Enter to Todo" style={{ borderWidth: 2, borderRadius: 10, backgroundColor: 'skyblue', height: 40, paddingLeft: 10, borderColor: 'black' }}></TextInput>
</View>
<View style={{ alignItems: 'center' }}>
<TouchableOpacity onPress={() => addElement()} style={{ marginTop: 10 }}>
<View style={{ backgroundColor: 'black', width: 70, height: 40, justifyContent: 'center', borderRadius: 10 }}>
<Text style={{ color: 'white', textAlign: 'center' }}>Add</Text>
</View>
</TouchableOpacity>
</View>
<FlatList
style={{ borderWidth: 2, padding: 10, flex: 1, backgroundColor: '#EEDDD0', marginTop: 10 }}
data={exampleState}
renderItem={renderItem}
keyExtractor={item => item.id.toString()
}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 20,
margin: 5,
}
})
export default App;
React Native provides a local storage option called AsyncStorage. You might use asyncstorage to save the data locally on the device and get that data inside useEffect() hook on startup.
You can find more about AsyncStorage here.
Although this is deprecated and now community package " #react-native-async-storage/async-storage " is used. The implementation remains the same.

How to Pass Props to Modal Component in React Native

I need to pass Flatlist data to modal in react native. Like, on click the item from the flatlist, it shows the modal with data for selected item. Here is my code below
Home.js
import React, { Component } from 'react';
import { StyleSheet, Text, View, FlatList, Image, TouchableOpacity, Alert, Button } from 'react-native';
import { Popup } from './Modal';
import * as productdata from '../data/productdata.js';
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: [],
};
}
componentDidMount() {
this.setState({
isLoading: false,
dataSource: productdata.product,
})
}
popupRef = React.createRef()
onShowPopup = item => {
popupRef.show();
}
onClosePopup = () => {
popupRef.close()
}
ProductList = ({ item }) => (
<View style={styles.listItem}>
<TouchableOpacity onPress={() => this.onShowPopup(item)} style={{ height: 100, width: 100, justifyContent: "center", alignItems: "center" }}>
<Image source={item.photo} style={{ width: 100, height: 100, borderRadius: 15 }} />
</TouchableOpacity>
<View style={{ alignItems: "center", flex: 1, marginTop: 20 }}>
<Text style={{ fontWeight: "bold", fontSize: 22 }}>{item.name}</Text>
<Text style={{ fontSize: 18, fontWeight: "bold" }}>{item.price}</Text>
</View>
<Popup name="Product Details" ref={(target) => popupRef = target} onTouchOutside={this.onClosePopup} />
</View>
);
render() {
return (
<View style={styles.container}>
<FlatList
data={this.state.dataSource}
renderItem={this.ProductList}
keyExtractor={item => item.name}
/>
</View>
);
}
}
Modal.js
import React, { Component } from 'react';
import { Modal, Dimensions, TouchableWithoutFeedback, StyleSheet, View, Text, Button, Image, TouchableOpacity } from 'react-native';
const deviceHeight = Dimensions.get("window").height
export class Popup extends Component {
constructor(props) {
super(props)
this.state = {
show: false,
}
}
show = () => {
this.setState({ show: true })
}
close = () => {
this.setState({ show: false })
}
renderOutsideTouchable(onTouch) {
const view = <View style={{ flex: 1, width: '100%' }} />
if (!onTouch) return view
return (
<TouchableWithoutFeedback onPress={onTouch} style={{ flex: 1, width: '100%' }}>
{view}
</TouchableWithoutFeedback>
)
}
renderTitle = () => {
const { name } = this.props
return (
<View style={{ alignItems: 'center' }}>
<Text style={{
color: 'black', fontSize: 20,
fontWeight: 'bold', margin: 15
}}>
{name}
</Text>
</View>
)
}
renderContent = (item) => {
return (
<View style={{ alignItems: 'center', marginBottom: 10 }}>
<View style={styles.card}>
<Text style={{ fontSize: 20, fontWeight: '500', fontWeight: 'bold', alignSelf: 'center', margin: 5 }}/>
<TouchableOpacity style={styles.buttonContainer}>
<Text style={styles.button}>Add to Cart</Text>
</TouchableOpacity>
</View>
</View>
)
}
render() {
let { show } = this.state
const { onTouchOutside, title } = this.props
return (
<Modal animationType={'slide'} transparent={true} visible={show} onRequestClose={this.close}>
<View style={{ flex: 1, backgroundColor: '#000000AA', justifyContent: 'flex-end' }}>
{this.renderOutsideTouchable(onTouchOutside)}
<View style={{
backgroundColor: '#FFFFFF', width: '100%', height: '70%', borderTopRightRadius:20, borderTopLeftRadius: 20, paddingHorizontal: 20, maxHeight: deviceHeight * 5}}>
{this.renderTitle()}
{this.renderContent()}
</View>
</View>
</Modal>
)
}
}
My Problem: I am not able to pass the flatlist item data to a Modal component and have no better idea solving it in this code.
Please help me and if any including, changes or complete solution for perfect understanding for the requirement would be really great. Many Thanks in Advance!
You don't need to duplicate open state in modal/popup. Simply set the style and open. Assume open close state is controlled by parent component, so if rendering modal/popup it is open by definition.
class Popup extends React.Component {
renderOutsideTouchable(onTouch) {
const view = <View style={{ flex: 1, width: '100%' }} />;
if (!onTouch) return view;
return (
<TouchableWithoutFeedback
onPress={onTouch}
style={{ flex: 1, width: '100%' }}>
{view}
</TouchableWithoutFeedback>
);
}
renderTitle = () => {
const { name } = this.props;
return (
<View style={{ alignItems: 'center' }}>
<Text
style={{
color: 'black',
fontSize: 20,
fontWeight: 'bold',
margin: 15,
}}>
{name}
</Text>
</View>
);
};
renderContent = () => {
const { item } = this.props;
return (
<View style={{ alignItems: 'center', marginBottom: 10 }}>
<View style={styles.card}>
<Text
style={{
fontSize: 20,
fontWeight: '500',
// fontWeight: "bold",
alignSelf: 'center',
margin: 5,
}}
/>
<TouchableOpacity style={styles.buttonContainer}>
<Text>Name: {item.name}</Text>
<Text>Price: {item.price}</Text>
<Text>Description: {item.desc}</Text>
<Text>Rating: {item.rating}</Text>
<Text style={styles.button}>Add to Cart</Text>
</TouchableOpacity>
</View>
</View>
);
};
render() {
const { onTouchOutside, title } = this.props;
return (
<Modal
animationType={'slide'}
transparent
visible // <-- visible prop is truthy
onRequestClose={this.close}>
<View
style={{
flex: 1,
backgroundColor: '#000000AA',
justifyContent: 'flex-end',
zIndex: 1000,
}}>
{this.renderOutsideTouchable(onTouchOutside)}
<View
style={{
backgroundColor: '#FFFFFF',
width: '100%',
height: '70%',
borderTopRightRadius: 20,
borderTopLeftRadius: 20,
paddingHorizontal: 20,
maxHeight: deviceHeight * 5,
}}>
{this.renderTitle()}
{this.renderContent()}
</View>
</View>
</Modal>
);
}
}
A react ref isn't necessary for opening a modal/popup to display a specific item. Change the onShowPopup and onClosePopup to set/nullify a clicked on item. Conditionally render the Popup outside the Flatlist.
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
dataSource: productdata.product,
isLoading: false,
popupItem: null,
};
}
onShowPopup = (popupItem) => {
this.setState({ popupItem });
};
onClosePopup = () => {
this.setState({ popupItem: null });
};
ProductList = ({ item }) => (
<View style={styles.listItem}>
<TouchableOpacity
onPress={() => this.onShowPopup(item)}
style={{
height: 100,
width: 100,
justifyContent: 'center',
alignItems: 'center',
}}>
<Image
source={item.photo}
style={{ width: 100, height: 100, borderRadius: 15 }}
/>
</TouchableOpacity>
<View style={{ alignItems: 'center', flex: 1, marginTop: 20 }}>
<Text style={{ fontWeight: 'bold', fontSize: 22 }}>{item.name}</Text>
<Text style={{ fontSize: 18, fontWeight: 'bold' }}>{item.price}</Text>
</View>
</View>
);
render() {
return (
<View style={styles.container}>
{this.state.popupItem && (
<Popup
name="Product Details"
item={this.state.popupItem} // <-- pass pop item
onTouchOutside={this.onClosePopup}
/>
)}
<FlatList
data={this.state.dataSource}
renderItem={this.ProductList}
keyExtractor={(item) => item.name}
/>
</View>
);
}
}
Expo Snack

scroll when the keypad is open

On my screen, I type in the input field and get search results accordingly. The list is rendered within a ScrollView but it still doesn't let me scroll when the keypad is open (in Android at least).
How can I fix this?
This is the component where the scroll view is rendered.
export const LocationsFound: React.FunctionComponent<LocationsFoundProps> = ({
addressesFound,
}) => {
return (
<>
{addressesFound.length > 0 ? (
<KeyboardAwareScrollView
style={styles.searchResultsContainer}
keyboardShouldPersistTaps={'always'}
keyboardDismissMode={'on-drag'}
>
{addressesFound.map((addressDetails: addressDetailsType) => {
return (
<View
key={addressDetails.placeName}
style={styles.resultContainer}>
<Text
style={styles.text}
onPress={() => handleLocationSelection(addressDetails)}>
{addressDetails.placeName}
</Text>
</View>
);
})}
</KeyboardAwareScrollView>
) : null}
</>
);
};
const styles = StyleSheet.create({
searchResultsContainer: {
width: moderateScale(400),
paddingHorizontal: moderateScale(50),
paddingRight: moderateScale(65),
marginTop: moderateScale(10),
},
resultContainer: {
marginTop: moderateScale(10),
borderBottomWidth: 1,
borderBottomColor: 'grey',
},
text: {
fontSize: moderateScale(15),
},
});
This is the component where the LocationsFound component is called.
return (
<SafeAreaView style={styles.safeAreaViewContainer}>
<View style={styles.container}>
<View style={styles.searchFieldContainer}>
<AddressSearchInput
addressType="favouritePoint"
placeholder="Ort eingeben"
/>
</View>
<View style={styles.dropdown}>
<LocationsFound
addressesFound={locations.addressesFoundList}
/>
</View>
</View>
</SafeAreaView>
);
};
export const styles = StyleSheet.create({
safeAreaViewContainer: {
flex: 1,
},
container: {
height: '100%',
backgroundColor: 'white',
width: '100%',
display:"flex",
flexDirection:"column",
flex: 1
},
dropdown: {
position: 'absolute',
top: moderateScale(215),
zIndex: moderateScale(10),
backgroundColor: '#fff',
flex: 1
},
});
I also tried adding
onScrollBeginDrag={Keyboard.dismiss}
but it doesn't make a difference.
sounds like a height, issue, without all the code, no one is going to give your clear answers without speculation. the keyboard doesn't shrink the view, check out this package, it may help - https://github.com/APSL/react-native-keyboard-aware-scroll-view

How to send and display the value of a text input on another screen with native react?

I want to send the data of a text input that is stored in a variable to another screen and show the data that was saved in said variable
Try to pass the state of the variable by react-navigation but the problem is that I have 3 screens and I want to pass the data stored in the variable text from screen 1 to screen 3
class Screen1 extends Component {
constructor(props) {
super(props);
this.state = {
text: '',
percentage: ''
};
}
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state
var data = {
textData: params.text
}
return {
headerLeft: (
<View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }}>
<AntDesign
name="left"
color="rgba(0,0,0,0.5)"
size={30}
style={{ marginLeft: 5 }}
/>
<Text style={{ marginLeft: 20, color: '#000', fontSize: 17, fontWeight: 'bold' }}>Crear regalo</Text>
</View>
),
headerRight: (
<View>
<TouchableOpacity
disabled={navigation.getParam('isDisable')} // get value from params and pass it to disabled props
onPress={() => navigation.navigate('2', { data })}
style={styles.Btn}>
<Text style={styles.TxtBtn}>Siguiente</Text>
</TouchableOpacity>
</View>
),
}
};
// set by a default value in componentDidMount to make the next button disable initially
componentDidMount() {
Alert.alert('Advertencia', 'Ingrese el porcentaje de su descuento');
this.props.navigation.setParams({ isDisable: true });
}
render() {
return (
<View style={styles.container}>
<View style={styles.Box}>
<ImageBackground source={require('../Icons/Regalo.png')} style={styles.Image}>
<View style={styles.ContainerInput}>
<TextInput
style={{ textAlign: 'center', color: '#fff', fontSize: 40, }}
type="numeric"
placeholder="%"
value={this.state.text} //set value from state
onChangeText={(text) => {
//when text length is greater than 0 than next button active otherwise it will be disable
let isDisable = text.length > 0 ? false : true
//set value in the state
this.setState({ text: text })
// set value to params
this.props.navigation.setParams({ isDisable: isDisable });
}} />
<Text style={{ fontSize: 40, color: '#fff', textAlign: 'center' }}>
{this.state.text.split(' ').map((word) => word && '%').join(' ')}
</Text>
</View>
</ImageBackground>
</View>
</View>
);
}
}
export default Screen1;
My Screen Two:
class Screen2 extends Component {
constructor(props) {
super(props);
this.state = {
textData: this.props.navigation.state.params.data.textData,
text: ''
};
}
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state
var data = {
textData: params.textData
}
return {
headerLeft: (
<View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }}>
<AntDesign
name="left"
color="rgba(0,0,0,0.5)"
size={30}
style={{ marginLeft: 5 }}
onPress={() => navigation.navigate('1')}
/>
<Text style={{ marginLeft: 20, color: '#000', fontSize: 17, fontWeight: 'bold' }}>Crear regalo</Text>
</View>
),
headerRight: (
<View>
<TouchableOpacity
disabled={navigation.getParam('isDisable')} // get value from params and pass it to disabled props
onPress={() => navigation.navigate('3', { data })}
style={styles.Btn}>
<Text style={styles.TxtBtn}>Crear</Text>
</TouchableOpacity>
</View>
),
}
};
componentDidMount() {
this.props.navigation.setParams({ isDisable: true });
}
render() {
return (
<View style={styles.container}>
<View style={styles.InputContainer}>
<TextInput
multiline
style={styles.Input}
type="numeric"
placeholder="Describa los términos y condificones de tu regalos"
placeholderTextColor="rgb(196,196,196)"
value={this.state.text} //set value from state
onChangeText={(text) => {
//when text length is greater than 0 than next button active otherwise it will be disable
let isDisable = text.length > 1 ? false : true
//set value in the state
this.setState({ text: text })
// set value to params
this.props.navigation.setParams({ isDisable: isDisable });
}} />
</View>
</View>
);
}
}
export default Screen2;
My Screen Three:
class Screen3 extends Component {
constructor(props) {
super(props);
this.state = {
textData: this.props.navigation.state.params.data.textData,
text: '',
percentage: '',
};
}
static navigationOptions = ({ navigation }) => ({
headerLeft: (
<View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }}>
<AntDesign
name="left"
color="rgba(0,0,0,0.5)"
size={30}
style={{ marginLeft: 5 }}
onPress={() => navigation.navigate('2')}
/>
<Text style={{ marginLeft: 20, color: '#000', fontSize: 17, fontWeight: 'bold' }}>Crear regalo</Text>
</View>
),
headerRight: (
<View>
<TouchableOpacity
disabled={navigation.getParam('isDisable')} // get value from params and pass it to disabled props
onPress={() => navigation.navigate('4')}
style={styles.Btn}>
<Text style={styles.TxtBtn}>Crear</Text>
</TouchableOpacity>
</View>
),
});
// set by a default value in componentDidMount to make the next button disable initially
componentDidMount() {
this.props.navigation.setParams({ isDisable: true });
}
render() {
let datos = [{
value: 'Banana',
}, {
value: 'Mango',
}, {
value: 'Pear',
}];
var data = {
textData: this.state.textData
}
return (
<View style={styles.container}>
<View style={styles.BoxandInput}>
<View style={styles.ContainerInput}>
<TextInput
style={styles.Input}
multiline
placeholder='Escriba una Descripcion'
placeholderTextColor="rgb(196,196,196)"
maxLength={203}
/>
</View>
<View style={styles.Box}>
<ImageBackground
source={require('../Icons/Regalo.png')}
style={styles.Image}>
<View style={styles.ContainerText}>
<Text style={styles.TextBox}>{data}</Text>
</View>
</ImageBackground>
</View>
</View>
<View style={styles.Regalos}>
<View style={{ justifyContent: 'center', alignItems: 'center', flexDirection: 'row' }}>
<Text style={styles.line}>--------------------------</Text>
<Text style={styles.RegalosText}>Cantidad de Regalos</Text>
<Text style={styles.line}>--------------------------</Text>
</View>
<View style={{ justifyContent: 'center', alignItems: 'center', marginTop: 30 }}>
<TextInput
style={styles.RegalosInput}
placeholder='0'
placeholderTextColor='#000'
maxLength={6}
type='numeric'
/>
<View style={styles.LineInput} />
<Text style={{ fontSize: 10, color: '#ccc', textAlign: 'center', marginTop: 5 }}>60 regalos Disponibles</Text>
<TouchableOpacity style={{ marginTop: 15 }}>
<Text style={{ fontSize: 10, color: 'cyan', textAlign: 'center' }}>Comprar Regalos</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.ContainerServices}>
<View style={{ justifyContent: 'center', alignItems: 'center', flexDirection: 'row' }}>
<Text style={styles.line}>-----------------------------------</Text>
<Text style={styles.RegalosText}>Servicios</Text>
<Text style={styles.line}>-----------------------------------</Text>
</View>
<Dropdown
dropdownMargins={{ min: 5, max: 10 }}
label='Favorite Fruit'
data={datos}
/>
</View>
</View>
);
}
}
export default
This Problem
You can create a data object and you can send it with navigation to next UI. You can pass the data object from UI 1 to UI 2 and then you can send it UI 3 from UI 2.
In the first UI you can create data variable like,
var data = {
textData: text
}
Include the above code inside navigationOptions of your code.
Then in the navigation call include the data object as well.
onPress={() => navigation.navigate('2', {data})}
In the second UI assign the passed value to a variable inside the constructor.
constructor(props) {
super(props);
this.state = {
textData: this.props.navigation.state.params.data. textData,
}
}
Then when you are going to navigate to the third UI create a data variable and send it as previously done.
var data = {
textData: this.state.textData
}
Pass this variable with navigate.navigation and access it from third UI similar as in the second UI.

How do I overlay ActivityIndicator in react-native?

I have a View with few form elements and a button (TouchableHighlight). On clicking the button, an Activity Indicator should be shown as an overlay to the existing view. The Activity Indicator should be centered within the page and the existing view should be slightly blurred to indicate overlay. I tried different options but could not get it to work.
render() {
const { username, password } = this.state;
return (
<View style={styles.container}>
<View style={styles.group}>
<Text style={styles.text}>Username:</Text>
<TextInput
style={styles.input}
onChangeText={this.handleUserNameChange.bind(this)}
value={username}
underlineColorAndroid="transparent"
/>
</View>
<View style={styles.group}>
<Text style={styles.text}>Password:</Text>
<TextInput
style={styles.input}
secureTextEntry={true}
onChangeText={this.handlePasswordChange.bind(this)}
value={password}
underlineColorAndroid="transparent"
/>
</View>
<TouchableHighlight
style={styles.button}
onPress={this.handleLogin.bind(this)}>
<Text style={styles.buttonText}>Logon</Text>
</TouchableHighlight>
</View>
);
}
Existing styles:
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'flex-start',
alignItems: 'center',
backgroundColor: '#F5FCFF',
marginTop: 60
},
group: {
alignItems: 'flex-start',
padding: 10
},
input: {
width: 150,
padding: 2,
paddingLeft: 5,
borderColor: 'gray',
borderWidth: 1
},
text: {
padding: 0
},
button: {
width: 150,
backgroundColor: '#2299F2',
padding: 15,
marginTop: 20,
borderRadius: 5
},
buttonText: {
textAlign: 'center',
color: '#fff',
fontSize: 24
},
});
I need to an ActivityIndicator to the above View, overlay the view, and center the ActivityIndicator.
For this to work, you'd need to absolute position it, and render it after the elements that should be underneath the overlay:
loading: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center'
}
Then simply compose it into the render method conditionally, based on a loading state. I am going to assume this.handleLogin sets some sort of loading state already.
Make sure it's rendered last so it takes precedence.
...
{this.state.loading &&
<View style={styles.loading}>
<ActivityIndicator size='large' />
</View>
}
Here is a complete example using create react native app.
import React from 'react';
import {StyleSheet, ActivityIndicator, View} from "react-native";
export default class Example extends React.Component {
constructor(props) {
super(props);
this.state = {}
render() {
return (
<View
style={{flex: 1}}
>
//Add other content here
{this.state.loading &&
<View style={styles.loading}>
<ActivityIndicator/>
</View>
}
</View>
);
}
}
showLoading() {
this.setState({loading: true})
}
hideLoading() {
this.setState({loading: false})
}
const styles = StyleSheet.create({
loading: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
opacity: 0.5,
backgroundColor: 'black',
justifyContent: 'center',
alignItems: 'center'
}
})
You can use StyleSheet.absoluteFill to shorten code.
Add this to your render:
<View style={styles.container}>
//... other code here
{this.state.loading && <View
style={{
...StyleSheet.absoluteFill,
justifyContent: 'center',
alignItems: 'center',
}}>
<ActivityIndicator />
</View>}
</View>
Improvement:
You can also create a Loading component:
Loading.js
import React from 'react';
import {View, ActivityIndicator, StyleSheet} from 'react-native';
export const Loading = ({theme = 'white', size = 'large'}) => {
const color = theme === 'white' ? '#00bdcd' : '#fff';
return (
<View
style={{
...StyleSheet.absoluteFill,
justifyContent: 'center',
alignItems: 'center',
}}>
<ActivityIndicator size={size} color={color} />
</View>
);
};
Then use it anywhere you want
<View style={styles.container}>
//... other code here
// remember to import Loading component
{this.state.loading && <Loading />}
</View>
You can build a nice overlay using the activity indicator component by also leveraging the modal capabilities like Sanaur suggests.
For example you can use the below functional component. You can control it's visibility through the show prop that you can tie to a state in your screen.
An example that you can adapt to your needs.
const ModalActivityIndicator = props => {
const {
show = false,
color = "black",
backgroundColor = "white",
dimLights = 0.6,
loadingMessage = "Doing stuff ..."
} = props;
return (
<Modal transparent={true} animationType="none" visible={show}>
<View
style={{
flex: 1,
alignItems: "center",
justifyContent: "center",
backgroundColor: `rgba(0,0,0,${dimLights})`
}}
>
<View
style={{
padding: 13,
backgroundColor: `${backgroundColor}`,
borderRadius: 13
}}
>
<ActivityIndicator animating={show} color={color} size="large" />
<Text style={{ color: `${color}` }}>{loadingMessage}</Text>
</View>
</View>
</Modal>
);
};
and in your screen, in the render's return, just add it there as a child (please ignore the rest of the code, I put it there for context).
return (
<TouchableWithoutFeedback
onPress={() => {
Keyboard.dismiss();
}}
>
<View style={{ padding: 13, flex: 1}}>
<ModalActivityIndicator show={screenIsWaiting} />
<View
style={{
where screenIsWaiting is just a state, for example
const [screenIsWaiting, setScreenIsWaiting] = useState(false);
To test it you can add a button somewhere,
<Button
title="TestButton"
onPress={async () => {
setScreenIsWaiting(true);
await sleep(5000);
setScreenIsWaiting(false);
...
}}
/>
where sleep is a function defined as
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
I found the sleep() idea on stackoverflow on another post.
You can of course also define the
<ModalActivityIndicator show={screenIsWaiting} ... />
only once in your App's root component and trigger it's display and props via a global state container like redux.
There is a library available for this react-native-loading-spinner-overlay.
You can simply install it using
npm install react-native-loading-spinner-overlay --save
and can import into your project using
import Spinner from 'react-native-loading-spinner-overlay';
Here is how to use it
<Spinner
//visibility of Overlay Loading Spinner
visible={this.state.loading}
//Text with the Spinner
textContent={'Loading...'}
//Text style of the Spinner Text
textStyle={styles.spinnerTextStyle}
/>
STEP 1:
Create the component for the spinner:
export const OverlaySpinner = () => {
return (
<View style={styles.spinnerView}>
<ActivityIndicator size="large" color="#0000ff" />
</View>
);
};
STEP 2:
Create the style for the spinner view (using zIndex is very important to make sure the view is over everything on the screen):
spinnerView: {
position: "absolute",
zIndex: 1,
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: "center",
justifyContent: "center",
backgroundColor: "#F5FCFF88",
},
STEP 3:
Make the initial state for the spinning component:
const [showSpinner, setshowSpinner] = useState(true);
STEP 4:
Use the component and don't forget to import it (don't forget to dismiss the keyboard on submit)
{showSpinner && <OverlaySpinner />}
I suppose you should use Modal to overlay activity indicator. Following is an example:
<Modal
transparent={true}
animationType={'none'}
visible={loading}
onRequestClose={() => {console.log('close modal')}}>
<View style={styles.modalBackground}>
<View style={styles.activityIndicatorWrapper}>
<ActivityIndicator
animating={loading} />
</View>
</View>
</Modal>
Add in view of loading
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center'
set in View of Activity Indicator
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center'
Here my code for a functional component for anyone looking to achieve this with nativebase as design system. useColorScheme is another hook I use for detecting dark mode.
import { Flex, Spinner } from "native-base"
import React from "react"
import useColorScheme from "../../hooks/useColorScheme"
export default function Loading() {
const colorScheme = useColorScheme()
return (
<Flex
position="absolute"
alignItems="center"
justifyContent="center"
top={0}
left={0}
right={0}
bottom={0}
backgroundColor={colorScheme === "dark" ? "coolBlack.500" : "white"}
>
<Spinner size="lg" color={colorScheme === "dark" ? "white" : "coolBlack.500"} />
</Flex>
)
}

Categories

Resources