On a button click, I start a process that takes sometime and once it gets finished I navigate to a specified screen.
While under process, I Alert a "loading" prompt which according to the docs: "By default, the only button will be an 'OK' button". And once the process is done, I alert again that the data is ready.
Problem is that I get two prompts above each other, and that they need user's click to be removed.
I would like to remove the first prompt before displaying the second (and maybe set a timer for the second to then remove it as well).
How can I remove Alert prompts programmatically?
A basic workaround in order to achieve what you're asking for would be to create a separate component that will act as the alert instead.
The component that I've written accepts two props. text and visible.
Inside your screen you can add it as so:
import React from 'react'
[....]
export default class Screen extends React.Component {
state = {
visible: true,
text: "Loading... Please wait"
}
drawAlert() {
setTimeout(() => {
this.setState({text: "Will dismiss in 1 second"}, () => {
setTimeout(() => {
this.setState({visible: false})
}, 1000);
})
}, 4000); // fake API request
return (
<Alert text={this.state.text} visible={this.state.visible} />
)
}
render() {
return(
<Alert text={this.state.text} visible={this.state.visible} />
)
}
}
This is the alert component
import React, { Component } from 'react'
import { View, TouchableOpacity, Text } from 'react-native'
export default class Alert extends Component {
state = {
text: this.props.text,
visible: this.props.visible
}
componentWillReceiveProps(nextProps) {
this.setState({text: nextProps.text, visible: nextProps.visible})
}
drawBox() {
if (this.state.visible) {
return(
<View style={styles.container}>
<View style={styles.boxContainer}>
<View style={styles.textContainer}>
<Text style={styles.text}>{this.state.text}</Text>
</View>
<TouchableOpacity onPress={this.props.onPress} style={styles.buttonContainer}>
<Text style={styles.buttonText}>OK</Text>
</TouchableOpacity>
</View>
</View>
)
}
}
render() {
return(
<View style={styles.container}>
{this.drawBox()}
</View>
)
}
}
const styles = {
wrapper: {
flex: 1,
},
container: {
zIndex: 99999,
position: "absolute",
backgroundColor: "rgba(0, 0, 0, 0.1)",
top: 0,
left: 0,
right: 0,
bottom: 0,
justifyContent: 'center',
alignItems: 'center',
},
boxContainer: {
backgroundColor: "#FFF",
borderRadius: 2,
padding: 10,
width: 300,
borderColor: "rgba(0,0,0,.1)",
borderBottomWidth: 0,
shadowOffset: { width: 0, height: 2 },
elevation: 1,
padding: 20
},
textContainer: {
marginBottom: 20
},
text: {
color: "#424242",
fontFamily: "Roboto",
fontSize: 18
},
buttonContainer: {
alignItems: 'flex-start'
},
buttonText: {
color: "#009688"
}
}
Hope it helps.
Related
I am making an app which has some buttons that play different sounds, and a stop button which stops all the sounds. However, it works only when the current sound playing stops, and does not play any other music. Is the function incorrect? Here is the code(the other buttons are written in the same way as the first button):
import React, { Component } from 'react';
import { Button, View, Text, Alert, TouchableOpacity } from 'react-native';
import {Audio} from "expo-av";
class Button1 extends React.Component {
playSound1 = async () => {
await Audio.Sound.createAsync(
{uri:"https://s1.yt.ninja/#download/23481-602b351bd79f3-10112020-252-320-file-10112020/mp3/lcVNSPXM2Nc/The%2BUntamed%2BOST%2B%257C%2B%25E9%2599%2588%25E6%2583%2585%25E4%25BB%25A4%2BMain%2BThemed%2BSong%25E3%2580%258A%25E6%2597%25A0%25E7%25BE%2581%2BWu%2BJi%25E3%2580%258B%25E2%2580%2594%25E2%2580%2594Xiao%2BZhan%2B%25E3%2580%2581Wang%2BYi%2BBo%2BDuet.mp3/9f05bbbdbd17b34a35bd40794186a567e755c50ee15ef6c77345bf1eaf7e8124-1"},
{shouldPlay:true}
)
}
render() {
return (
<TouchableOpacity style={{
backgroundColor : "#D1A5C9",
marginTop: 30,
marginLeft: 25,
width: 280,
height: 40,
alignItems: "center"
}}
onPress={this.playSound1}>
<Text style={{
color:"white",
fontSize: 30,
}}>Sound 1</Text>
</TouchableOpacity>
);
}
}
class StopButton extends React.Component{
render(){
return(
<TouchableOpacity style={{
backgroundColor : "black",
marginTop: 50,
marginLeft: 40,
width: 250,
height: 40,
alignItems: "center"
}}
onPress={() => {
Audio.setIsEnabledAsync(false)
}}>
<Text style={{
color:"white",
fontSize: 30,
}}>Stop Sound</Text>
</TouchableOpacity>
)
}
}
export default class App extends React.Component {
render() {
return (
<View>
<Button1/>
<Button2/>
<Button3/>
<Button4/>
<Button5/>
<StopButton/>
</View>
);
}
}
It will work, but only on your phone, not the simulators or the web options
But once you stop it, it wont start sound again ever, unless you add this
class SoundButton6 extends Component {
render() {
return (
<TouchableOpacity
style={{
backgroundColor: 'red',
marginLeft: 100,
borderWidth: 1,
borderColor: 'black',
alignItems: 'center',
justifyContent: 'center',
width: 200,
height: 100,
borderRadius: 100,
marginTop: 10,
}}
onPress={ () => {
Audio.setIsEnabledAsync(true);
}}>
<Text
style={{
fontWeight: 'bold',
fontSize: 35,
}}>
Resume
</Text>
</TouchableOpacity>
);
}
}
a resume button
Have a try by using the instance of the Audio.Sound to stop the audio.
For Ex:
const {
sound: soundObject,
status,
} = await Audio.Sound.createAsync(require('./assets/sounds/hello.mp3'), { shouldPlay: true });
and on the stop button action process the soundObject to stop the music:
onStop = async () => {
soundObject.stopAsync()
}
for more details check out the official docs from expo AV
https://docs.expo.io/versions/latest/sdk/audio/#audiosetaudiomodeasyncmode
The error occurs when I try to close the modal window. on the "Close" button. The error says that _this.state.data [imageKey] .urls' is not an object, but I pass the string.
I am receiving JSON from a custom API endpoint. I was able to iterate over the JSON and output some data, but the problem is with the string reference to the image. I am getting the error:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
TouchableWithoutFeedback,
Dimensions,
Modal,
ScrollView,
} from 'react-native';
import ImageElement from './component/ImageElement';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
modalVisible: false,
data: [],
};
}
componentDidMount() {
fetch('https://api.unsplash.com/photos/?client_id=cf49c08b444ff4cb9e4d126b7e9f7513ba1ee58de7906e4360afc1a33d1bf4c0')
.then((response) => response.json())
.then((data) => {
this.setState({
data: data,
});
});
}
// getImage() {
// return this.state.modalImage
// }
setModalVisible = (visible, imageKey) => {
this.setState({ modalImage: this.state.data[imageKey].urls.raw });
this.setState({ modalVisible: visible });
};
render() {
let images = this.state.data.map((val, key) => {
return (
<TouchableWithoutFeedback
key={key}
onPress={() => {
this.setModalVisible(true, key);
}}
>
<View style={styles.imagewrap}>
<ImageElement
author={{ url: val.user.profile_image.small }} // фото автора
imgsource={{ url: val.urls.small }} // работа автора
authorNameProp={val.user.name} // имя автора
></ImageElement>
</View>
</TouchableWithoutFeedback>
);
});
return (
<ScrollView>
<View style={styles.container}>
<Modal
style={styles.modal}
animationType={'fade'}
transparent={true}
visible={this.state.modalVisible}
onRequestClose={() => {}}
>
<View style={styles.modal}>
<Text
style={styles.text}
onPress={() => {
this.setModalVisible(false);
}}
>
Close
</Text>
<ImageElement imgsource={{ url: this.state.modalImage }}></ImageElement>
</View>
</Modal>
{images}
</View>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
flexWrap: 'wrap',
backgroundColor: 'grey',
overflow: 'scroll',
},
imagewrap: {
margin: 5,
padding: 2,
height: Dimensions.get('window').height / 2 - 60,
width: Dimensions.get('window').width / 2 - 10,
},
modal: {
flex: 1,
padding: 10,
backgroundColor: 'rgba(0,0,0, 0.9)',
},
text: {
color: '#fff',
zIndex: 20,
position: 'absolute',
top: 50,
right: 30,
fontSize: 20,
},
user: {
color: '#fff',
zIndex: 20,
position: 'absolute',
top: 20,
left: 10,
},
});
AppRegistry.registerComponent('ImageGallery', () => ImageGallery);
Issue
Seems you missed passing an index in your close handler this.setModalVisible(false), imageKey is undefined, and thus this.state.data[undefined] is undefined and throws error when you access deeper into the nested properties.
Solution
Conditionally access the data value based on the visible value. Clear out the modalImage state when closing the modal.
setModalVisible = (visible, imageKey) => {
this.setState({
modalImage: visible ? this.state.data[imageKey].urls.raw : null,
modalVisible: visible,
});
}
Alternatively you can just use Optional Chaining to do null checks for you.
setModalVisible = (visible, imageKey) => {
this.setState({
modalImage: this.state.data[imageKey]?.urls?.raw,
modalVisible: visible,
});
}
I only started learning React native a week ago and here i'm trying to add animation to an app that loads films on search from the TMDB api, it works fine, but when trying the animation and after i press the search button it displays and error saying :
undefined is not an object (evaluating 'new _reactNativeReanimated.Animated.Value')
* components\FilmItem.js:18:17 in constructor
to be honest i searched on the net but i didn't find a similar problem so can anyone help ?
here's my code :
ps: the app works perfectly fine before trying the animation, i added the comments ' //>>> ' to the code i added for the animation, also i added some screens
search screen before validating a search
error screen
// Components/FilmItem.js
import React from "react";
import {
StyleSheet,
View,
Text,
Image,
TouchableOpacity,
Dimensions
} from "react-native";
import { getImageFromApi } from "../Api/TMDBApi";
import { Animated } from "react-native-reanimated";
class FilmItem extends React.Component {
//>>> added the constructor
constructor(props) {
super(props);
this.state = {
positionLeft: new Animated.Value(Dimensions.get("window").width)
};
}
//>>> added the componentDidMount()
componentDidMount() {
Animated.spring(
this.state.positionLeft, {
toValue: 0
}).start()
}
_displayFavoriteImage() {
if (this.props.isFilmFavorite) {
return (
<Image
source={require("../images/icons8-heart-filled.png")}
style={styles.favorite_image}
/>
);
}
}
render() {
const film = this.props.film;
const displayDetailForFilm = this.props.displayDetailForFilm;
return (
//>>> encapsulated the Touchable opacity inside a Animated.view with a style
<Animated.View style={{ left: this.state.positionLeft }}>
<TouchableOpacity
onPress={() => displayDetailForFilm(film.id)}
style={styles.main_container}
>
<Image
style={styles.image}
source={{ uri: getImageFromApi(film.poster_path) }}
/>
<View style={styles.content_container}>
<View style={styles.header_container}>
{this._displayFavoriteImage()}
<Text style={styles.title_text}>{film.title}</Text>
<Text style={styles.vote_text}>{film.vote_average}/10</Text>
</View>
<View style={styles.description_container}>
<Text style={styles.description_text} numberOfLines={6}>
{film.overview}
</Text>
{/* La propriété numberOfLines permet de couper un texte si celui-ci est trop long, il suffit de définir un nombre maximum de ligne */}
</View>
<View style={styles.date_container}>
<Text style={styles.date_text}>Sorti le {film.release_date}</Text>
</View>
</View>
</TouchableOpacity>
</Animated.View>
)
}
}
const styles = StyleSheet.create({
main_container: {
height: 190,
flexDirection: "row"
},
image: {
width: 120,
height: 180,
margin: 5,
backgroundColor: "gray"
},
content_container: {
flex: 1,
margin: 5
},
header_container: {
flex: 3,
flexDirection: "row"
},
title_text: {
fontWeight: "bold",
fontSize: 20,
flex: 1,
flexWrap: "wrap",
paddingRight: 5
},
vote_text: {
fontWeight: "bold",
fontSize: 16,
color: "#666666"
},
description_container: {
flex: 7
},
description_text: {
fontStyle: "italic",
color: "#666666"
},
date_container: {
flex: 1
},
date_text: {
textAlign: "right",
fontSize: 14
},
favorite_image: {
width: 25,
height: 25,
marginRight: 5
}
});
export default FilmItem;
I believe you are not importing your dependencies correctly - make sure you differentiate between default and named exports i.e.
import Animated, { Value } from 'react-native-reanimated'
then you would have e.g.
positionLeft: new Value(Dimensions.get("window").width)
See here for ES6 import syntax.
I'm studying React Native with this site https://www.tutorialspoint.com/react_native/react_native_animations.htm
However, there is a problem while i'm trying to open app on iPhone. According to error it cannot find variable, though it's imported.
import React, { Component } from 'react';
import { View, LayoutAnimation, TouchableOpacity, Text, StyleSheet} from 'react-native';
export default class Animations extends Component {
state = {
myStyle: {
height: 100,
backgroundColor: 'red'
}
};
expandElement = () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.spring);
this.setState({
myStyle: {
height: 400,
backgroundColor: 'red'
}
})
};
collapseElement = () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.linear);
this.setState({
myStyle: {
height: 100,
backgroundColor: 'red'
}
})
};
render() {
return (
<View>
<View>
<View style={this.state.myStyle}/>
</View>
<TouchableOpacity>
<Text style={styles.button} onPress = {this.expandElement}>
Expand
</Text>
</TouchableOpacity>
<TouchableOpacity>
<Text style={styles.button} onPress = {this.collapseElement}>
Collapse
</Text>
</TouchableOpacity>
</View>
)
}
}
const styles = StyleSheet.create({
button: {
borderWidth: 1,
borderColor: 'red',
color: 'red',
textAlign: 'center',
marginTop: 50,
padding: 10
}
});
Ah... I've found the problem. It was in other component which had styles but no elements to them and had no imported StylesSheet since I corrected it to new conditions but forgot about block with styles.
I am trying to call a function whenever i select an item from picker and display the selected item with alert.
Here's what i am doing :-
import React, { Component } from 'react';
import { AppRegistry, StyleSheet, TextInput, View, Alert, Button,Platform,ActivityIndicator, Text, Picker, ScrollView } from 'react-native';
export default class FirstProject extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
throttlemode:'',
}
}
GetSelectedThrottleModeItem=(throttlemode)=>{
Alert.alert(this.state.throttlemode)
}
render() {
return (
<View style={styles.MainContainerAddCamp}>
<Text style={{fontSize: 12}}> Throttle Mode</Text>
<Picker style={styles.PickerStyleClass}
selectedValue={this.state.throttlemode}
onValueChange={(throttlemodeValue, throttlemodeIndex) => this.GetSelectedThrottleModeItem(this.setState({throttlemode:throttlemodeValue}))}>
<Picker.Item label="Asap" value="asap" />
<Picker.Item label="Even" value="even" />
</Picker>
</View>
);
}
}
const styles = StyleSheet.create({
MainContainerAddCamp :{
flex:1,
margin: 10,
paddingTop: (Platform.OS === 'ios') ? 20 : 20,
padding: 5,
},
TextInputStyleClass: {
textAlign: 'left',
paddingLeft: 7,
marginBottom: 7,
height: 40,
borderWidth: 1,
borderColor: '#00BCD4',
},
PickerStyleClass:{
backgroundColor:'#87ceeb',
paddingLeft: 7,
marginBottom: 7,
height: 40,
borderWidth: 1,
borderColor: '#FF5722',
}
});
This code is displaying the previously selected value. How can i make it to display current selected value.
Please suggest where i have missed.
First of all setState method returns nothing. Second after calling setState method, you can't know whether state is changed or not, it's because of setState method is asynchronous. You can assign a callback to second argument of setState method to understand state changes.
import React, { Component } from 'react';
import { AppRegistry, StyleSheet, TextInput, View, Alert, Button,Platform,ActivityIndicator, Text, Picker, ScrollView } from 'react-native';
export default class FirstProject extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
throttlemode:'',
}
}
onPickerValueChange=(value, index)=>{
this.setState(
{
"throttlemode": value
},
() => {
// here is our callback that will be fired after state change.
Alert.alert("Throttlemode", this.state.throttlemode);
}
);
}
render() {
return (
<View style={styles.MainContainerAddCamp}>
<Text style={{fontSize: 12}}> Throttle Mode</Text>
<Picker style={styles.PickerStyleClass}
selectedValue={this.state.throttlemode}
onValueChange={this.onPickerValueChange}>
<Picker.Item label="Asap" value="asap" />
<Picker.Item label="Even" value="even" />
</Picker>
</View>
);
}
}
const styles = StyleSheet.create({
MainContainerAddCamp :{
flex:1,
margin: 10,
paddingTop: (Platform.OS === 'ios') ? 20 : 20,
padding: 5,
},
TextInputStyleClass: {
textAlign: 'left',
paddingLeft: 7,
marginBottom: 7,
height: 40,
borderWidth: 1,
borderColor: '#00BCD4',
},
PickerStyleClass:{
backgroundColor:'#87ceeb',
paddingLeft: 7,
marginBottom: 7,
height: 40,
borderWidth: 1,
borderColor: '#FF5722',
}
});
Alert is displaying the old value because it is being called before this.setState({throttlemode:throttlemodeValue}) is done. So the correct way of doing is
GetSelectedThrottleModeItem=(throttlemodeValue)=>{
Alert.alert(throttlemodeValue)
this.setState({throttlemode:throttlemodeValue})
}
render() {
return (
<View style={styles.MainContainerAddCamp}>
<Text style={{fontSize: 12}}> Throttle Mode</Text>
<Picker style={styles.PickerStyleClass}
selectedValue={this.state.throttlemode}
onValueChange={(throttlemodeValue, throttlemodeIndex) =>
this.GetSelectedThrottleModeItem(throttlemodeValue)}>
<Picker.Item label="Asap" value="asap" />
<Picker.Item label="Even" value="even" />
</Picker>
</View>
);
}
Use componentDidUpdate to see the selected picker value like this
componentDidUpdate(){
this.handlePickerTest();
}
handlePickerTest = () => {
alert(this.state.throttlemode);
}