I am using Expo Audio to play some short sounds from a list.
async playAudio(file) {
try {
await Audio.setIsEnabledAsync(true);
const sound = new Audio.Sound();
await sound.loadAsync(file);
await sound.playAsync();
} catch(error) {
console.error(error);
}
}
Which I'm calling from a list rendered with list.map()
renderTheList = (item, i) => {
return (
<View key={i}>
<TouchableOpacity onPress={ () => { this.onAudioSelected(item.audio) }}>
</TouchableOpacity>
</View>
)
}
onAudioSelected(audio) {
// Audio.clearSounds() <-- something like this
playAudio(audio)
...
}
The sounds play fine, but when I select the next item in the list, the previous sound does not stop. So if I touch a bunch in a row, a bunch of sounds play at once.
How can I stop all currently playing sounds?
I figured out that I should create the playback object in the constructor and use unloadAsync()
constructor(props)
{
super(props);
this.audioPlayer = new Audio.Sound();
}
playSound = async () => {
try {
await this.audioPlayer.unloadAsync()
await this.audioPlayer.loadAsync(require("../soundfile.mp3"));
await this.audioPlayer.playAsync();
} catch (err) {
console.warn("Couldn't Play audio", err)
}
}
The full documentation is found at AV - Expo Documentation
Related
I can't even tell you how many variations I have tried, tutorials and documentations that I have watched and read, I cannot transfer data from one page to another. Am using react native expo. I have this included in both pages: import AsyncStorage from '#react-native-async-storage/async-storage';.
This is the page I'm trying to set the data:
const ToyDetails = () => {
const [savedName, setSavedName] = useState('')
const addCart = async() => {
setButtonText('ADDED TO CART!')
try {
await AsyncStorage.setItem('saved_name', savedName)
}catch(error){
console.log(error)
}
}
return(
<View>
<Text value={savedName}>{name}</Text>
#{name} is because I am importing the name from a FlatList item
</View>
)
}
And getting that data from another page:
const Cart = () => {
const [savedName, setSavedName] = useState('')
useEffect(()=>{
getData()
}, [])
const getData = () => {
try {
AsyncStorage.getItem('saved_name')
.then((value)=>{
if(value!=null){
setSavedName(value)
}
})
}catch(error){
console.log(error)
}
}
return (
<View>
<Text value={savedName} onChangeText={(value)=>setSavedName(value)}>{savedName}</Text>
</View>
)
}
I can post other variations I have tried if asked, I've tried adding it into a list and importing the list in the second page, I've tried to JSON.stringify the value savedName first (and JSON.parse it), I even tried doing it in the same way I did for FlatList. I'm not even getting any error messages.
in your ToyDetails.js while saving savedName is empty. i changed to name and able to get it on CartScreen
https://snack.expo.dev/7ozHrsOBT check ToyDetails.j file
const addCart = async() => {
setButtonText('ADDED TO CART!')
try {
console.log("savedName",savedName) //saved name is empty here
await AsyncStorage.setItem('saved_name', name)
}catch(error){
console.log('setitem didnt work')
}
}
For the Life of me, I cannot get this to render the data that I pull from the database.
Here is the code
function assetList(){
const [assetL, setAssetL] = useState([]);
const [errors, setErrors] = useState("");
const getAssets = async () =>{
try{
const list = [];
console.log("Break");
db.collection("assets").get().then(function(querySnapshot){
querySnapshot.forEach(function(doc){
list.push(doc.data());
});
});
//problem
setAssetL([...list]);
//problem
console.log("list");
console.log(list);
console.log("AssetL");
console.log
} catch (e) {
setErrors("Failed To Load Data");
}
};
useEffect(() => {
getAssets();
}, []);
console.log(assetL);
return(
<SafeAreaView style = {styles.Heading}>
<View style = {styles.Heading}>
<Text style = {styles.headText}>Asset List</Text>
</View>
<FlatList
data = {assetL}
renderItem={({item}) => <Text>{item.default}</Text>}
/>
</SafeAreaView>
);
}
I have narrowed down to at least the most pressing issue, other than my jank way of throwing this page of the app together, is that the setAssetL isnt actually setting the assetL const. Can anyone explain why this is happening and how to fix it?
For the getAssets function do something like this:
const getAssets = async () =>{
try {
const list = [];
console.log("Break");
db.collection("assets").get().then(function(querySnapshot){
querySnapshot.forEach(function(doc){
list.push(doc.data());
});
setAssetL(list);
});
...
} catch (e) {
setErrors("Failed To Load Data");
}
};
Your code did not work because db.collection("assets").get() function returns a promise and you handle it asynchronously while expecting it to be synchronous.
Here you can read more about async functions
First of all,i tried to make a watchlist for movies,a page that contains movies that you add.I can't get understand async storage and how it works within my code.
I've tried different tutorials,but I still don't get to make it work.I tried with the code that official react-native page has on their site,but with no results.
item2ADD = this.props.navigation.getParam('movie');
_retrieveData = async () => {
try {
await AsyncStorage.setItem('#MySuperStore:key', 'I like to save it.');
} catch (error) {
console.warn("data has not been saved");
}
try {
const value = await AsyncStorage.getItem('TASKS');
if (value !== null) {
console.warn("data has been loaded");
console.warn(value);
}
} catch (error) {
console.warn("data has not been loaded");
}
};
constructor(props) {
super(props);
this.state = {
data : [] ,
};
}
async componentDidMount() {
const data = this._retrieveData();
this.setState({ data });
}
render() {
if(this.state.data!==null) {
return (
<View style={styles.container}>
<Text style={styles.title}>Watchlist</Text>
<TouchableOpacity style={styles.backBtn} onPress={() => {
this.props.navigation.goBack()
}}>
<Image style={styles.backIMG} source={menuImages.back}/>
</TouchableOpacity>
<FlatList
data={this.state.data}
numColumns={2}
renderItem={({item}) =>
<View style={styles.fList}>
<TouchableOpacity onPress={() => this.props.navigation.navigate('Details', {movie: item})}>
<Image style={styles.img} source={{uri: item.coverUrl}}/>
</TouchableOpacity>
<Text style={styles.movieTitle}>{item.title}</Text>
<Text
style={styles.movieDate}>{moment.unix(Math.floor(parseInt(item.releaseDate)) / 1000).format("DD/MM/YYYY")}</Text>
</View>
} keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
else
{
return <Text>FAILED TO LOAD</Text>
}
}
}
I just want to know how can I implement AsyncStorage to store my added movies from the item2ADD,it contains only 1 item that i need to store in order to make a watchlist.(the item which contains all details that i need for a movie).
I want to save all the movies with asyncstorage then show them in that flatlist.
I keep getting :
invariant violation tried to get frame for out of range index nan(on android simulator) when i click to add a movie to the list.
I'm not sure what is the problem you are asking but there is a few things that maybe causing the problem.
1- In your function _retrieveData you don't return anything from it.
I'm not sure what you want to return but what you could do is return the value from the AsyncStorage.
_retrieveData = async () => {
try {
await AsyncStorage.setItem('#MySuperStore:key', 'I like to save it.');
} catch (error) {
console.warn("data has not been saved");
}
try {
const value = await AsyncStorage.getItem('TASKS');
if (value !== null) {
console.warn("data has been loaded");
console.warn(value);
}
// returning the value.
return value
} catch (error) {
console.warn("data has not been loaded");
}
};
2- You are setting state with the data from _retrieveData without waiting for it.
In you componentDidMount you set the data from _retrieveData but because it's a promise, you need to use await to get the data
async componentDidMount() {
// added await
const data = await this._retrieveData();
this.setState({ data });
}
3- The data you provide to FlatList needs to be an array.
When you call pass data={this.state.data} to FlatList, it needs to be an array, if it's not an array, you will have several problems.
Make sure that when you return the value from _retrieveData, it's an array.
// value needs to be an array
const value = await AsyncStorage.getItem('TASKS');
You should also notice that to setItem, it need to be parsed to a json, this means doing something like AsyncStorage.setItem('NAME_OF_YOUR_KEY', JSON.stringify(DATA_YOU_WANT_TO_STORE); and to getItem it need to be parsed to a javascript object, this means doing something like
const jsonValue = await AsyncStorage.getItem('NAME_OF_YOUR_KEY');
const value = JSON.parse(jsonValue);
i try fetch info from server and while fetching not complete show searching Component and when fetch done show information...everything is OK...But when NET offline show searching component and when turning on NET i want show a button "Try Again" and when click on "FetchData" function run again.
constructor(){
super();
this.state={
isLoading:true,
dataSource:null,
dataError:false
}
}
componentDidMount(){
FetchData = () => {
return fetch(SuperUrl+'/info')
.then((response)=>response.json())
.then((responseJson)=>{
this.setState({
isLoading:false,
dataSource: responseJson
})
})
.catch((error)=>{this.setState({dataError:true})})
}
FetchData()
}
render() {
if(this.state.dataError)
{
<View>
<Buttom onpress={()=>{FetchData()}}>
<Text>Try Again</Text>
<Button>
</View>
}
if(this.state.isLoading)
{
return(
<Container>
<StatusBar backgroundColor={'#3949ab'}/>
<Searching/>
<JaFooter list={{backgroundColor:'#3949ab', color:'#ffffff'}}/>
</Container>
)
}
else
{
let regionName = this.state.dataSource.map((value,key)=>{
return(
<ListItem key={key} >
<Text key={key} style={styles.cityName}>{value.name}</Text>
</ListItem>
)
})
Your title is very misleading as componentDidMount should run once. What you want is totally different so I'll explain. Since you are asking for something that goes against the pattern of RN you are risking getting your answer closed. Anyway..
Fetch does not support a timeout natively. But you can workaround it by running two promises. One for your fetch, and another for the timeout. This is pseudo code, you are gonna have to learn about Promise.race and how setTimeout works.
const promise1 = fetch;
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => reject(new TimeoutError()), 30000);
})
Promise.race([promise1, promise2])
.then(() => {
//You got a response from the server
})
.catch(err => {
//Probably timeout
if (err.code == 'Timeout')
this.setState({loading: false, showTryAgain: true})
})
Promise.race will run both in parallel but if the network request for the fetch ends first, it will ignore the timeout which is what you want.
I am trying to use the barcode scanner from react-native-camera. First, off it scans a QR-code and extracts a String, after that it navigates to the next Screen with react-navigation. In the second screen, it makes an API-call.
Now if I go back to the scanner screen, de QR-code will be scanned immediately. That's where I run into an error and the scanner freezes. I usually get this error:
Can't call setState (or forceUpdate) on an unmounted component
I think it's because my componentWillUnmount cleanup doesn't work properly or fast enough, but I already cancel the axios request.
requestCode = (code) => {
if (cancel != undefined) {
cancel();
}
axios.get(API_URI + code, {
cancelToken: new CancelToken(function executor(c) {
cancel = c;
})
}).then(response => {
console.log(response)
//checks if code was already called
this.checkUsed(response.data)
})
.catch(error => {
this.setState({ isValid: false })
});
}
componentWillUnmount() {
cancel();
}
Maybe I could mount the camera-scanner a little bit later so it doesn't scan this fast or is it maybe even an error with React Navigation?
You can use a flag to control.
class QR extends Component {
constructor(props) {
super(props)
this.state = {
scanable: true
}
this.cameraAttrs = {
ref: ref => {
this.camera = ref
},
style: styles.preview,
type: RNCamera.Constants.Type.back,
barCodeTypes: [RNCamera.Constants.BarCodeType.qr],
onBarCodeRead: ({ data }) => {
this.callback(data)
}
}
}
componentWillMount() {
this._mounted = true
}
componentWillUnmount() {
this._mounted = false
}
callback(text) {
if (!this.state.scanable) {
return
}
console.log(text)
this.setState({ scanable: false })
setTimeout(() => {
if (this._mounted) {
this.setState({ scanable: true })
}
}, 1000) // 1s cooldown
}
render() {
return (
<View style={styles.container}>
<RNCamera
{...this.cameraAttrs}
>
</RNCamera>
</View>
)
}
}