I have the following code to fill up a select with the available microphones
const audioInputSelect = document.querySelector('select#audioSource');
// Updates the select element with the provided set of cameras
function updateMicrophoneList(microphones) {
console.log(microphones);
audioInputSelect.innerHTML = '';
microphones.map(microphone => {
const microphoneOption = document.createElement('option');
microphoneOption.label = microphone.label;
microphoneOption.value = microphone.deviceId;
}).forEach(microphoneOption => audioInputSelect.add(microphoneOption));
}
// Fetch an array of devices of a certain type
async function getConnectedDevices(type) {
const devices = await navigator.mediaDevices.enumerateDevices();
return devices.filter(device => device.kind === type)
}
// Get the initial set of cameras connected
const microphonesList = getConnectedDevices('audioinput');
updateMicrophoneList(microphonesList);
// Listen for changes to media devices and update the list accordingly
navigator.mediaDevices.addEventListener('devicechange', event => {
const newMicrophoneList = getConnectedDevices('audioinput');
updateMicrophoneList(newMicrophoneList);
});
I'm getting the error
VM1759 audio_devices.js:7 Uncaught TypeError: microphones.map is not a function
at updateMicrophoneList (VM1759 audio_devices.js:7)
at VM1759 audio_devices.js:24
Why doesn't map work here?
getConnectedDevices is an async function, meaning that it returns a Promise instead of an array. You can use the .then function to update the list when the Promise is fulfilled.
getConnectedDevices('audioinput').then(updateMicrophoneList);
Related
I have a function that is run when a user clicks a button, when this function is run it gathers data and updates state. I then have another function which runs that uses some of the data that is added to state, the issue is the state is not updating in time so the function is using old data.
First function
async function callWeather() {
const key = "";
// Get location by user
let location = formData.location;
// Url for current weather
const currentWeatherUrl = `https://api.openweathermap.org/data/2.5/weather?q=${location}&units=metric&appid=${key}`;
// Get the current weather
const currentWeatherResponse = await fetch(currentWeatherUrl);
if (!currentWeatherResponse.ok) {
// Return this message if an error
const message = `An error has occured: ${currentWeatherResponse.status}`;
throw new Error(message);
}
const weatherDataResponse = await currentWeatherResponse.json();
// Update state with data
setWeatherData(weatherDataResponse);
}
Second function
async function callForcast() {
const key = "";
// Get lat & lon from the previous data fetch
const lon = weatherData.coord.lon
const lat = weatherData.coord.lat
// Get forcast data
const forcastWeatherUrl = `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&units=metric&appid=${key}`
const forcastWeatherResponse = await fetch(forcastWeatherUrl);
if (!forcastWeatherResponse.ok) {
const message = `An error has occured: ${forcastWeatherResponse.status}`;
throw new Error(message);
}
const forcastDataResponse = await forcastWeatherResponse.json();
// Update state with the forcast data
setForcastData(forcastDataResponse);
}
This then runs with the onClick calling both functions
function callWeatherAndForcast() {
callForcast();
callWeather();
}
use 'await' before calling callForcast so the second function (callWeather) does'nt get called immediately after calling first function.
async function callWeatherAndForcast() {
await callForcast();
callWeather();
}
also as #tromgy mentioned in the comments, React state updates are not immediate, try calling callWeather function inside a hook which has a dependency on forcastData state
Are you using FunctionComponent or Classes ?
Also, keep in mind that updating the state will trigger a rerendering. This means that:
The state update is not immediate
If one of your functions use the data from another, you should take care of these dependencies.
For helping you correctly, I need to know if you use FunctionComponent or Class and get the whole Function/Class.
Edit: based on the fact that you're using FunctionComponent.
In order to archive what you want, you need to use hooks.
Hooks are the way to handle a function component lifecycle.
For your problem, you'll need useState, useCallback hooks.
export const DisplayWeather = () => {
const [forecast, setForecast] = useState();
const [weather, setWeather] = useState();
const [error, setError] = useState();
const onSubmit = useCallback(async () => {
getWeather();
getForecast();
}, [forecast, weather]);
const getWeather = useCallback(async () => {
const key = "";
const location = formData.location;
const currentWeatherURL = `https://api.openweathermap.org/data/2.5/weather?q=${location}&units=metric&appid=${key}`;
const apiResponse = await fetch(currentWeatherURL);
if(!apiResponse.ok){
const message = `An error has occured: ${apiResponse.status}`;
setError(message);
} else {
const weatherData = apiResponse.json();
setWeather(weatherData);
}
}, [formData]);
const getForecast = useCallback(async () => {
const key = "";
const lon = weather.coord.lon;
const lat = weather.coord.lat;
const forecastWeatherUrl = `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&units=metric&appid=${key}`
const apiResponse = await fetch(forecastWeatherUrl);
if(!apiResponse.ok) {
const message = `An error has occured: ${apiResponse.status}`;
setError(message);
} else {
const forecastData = apiResponse.json();
setForecast(forecastData);
}
}, [weather]);
if(error){
return (
<p>Error: {error}</p>
)
}
return (
<p>Forecast data</p>
<p>{forecast.data.temperature}</p>
<p>Weather data</p>
<p>{weather.data.temperature}</p>
);
}
In the code above, I set 2 state variables (weather & forecast) and create 3 functions.
The onSubmit function is called when the user click. His callback depend on two variables (weather & forecast) which are referenced in the dependency array (the [] after the callback)
The getWeather function is called before getForecast because the result of the getForecast function depends on the weather state. That's why you have weather in the getForecast callback dependency array. It tells getForecast that when the value of weather change, it needs to re-render.
Note that i've added formData into the dependency array of getWeather otherwise, when the user click, the getWeather function won't get any value from formData.
Note: it is not a working example, just a simple explanation. You can find more infos here:
Hooks Reference
useCallback Reference
State does not update immediately! Meaning that the function I want to get the new state will get the previous state. To fix this I added callForcast function into a useEffect hook which has a dependency on callWeather because callForcast needs callWeather to update state first. This means when this function is run state will be updated in time.
useEffect (() => {
async function callForcast() {
const key = "";
// Get lat & lon from the previous data fetch
const lon = weatherData.coord.lon
const lat = weatherData.coord.lat
// Get forcast data
const forcastWeatherUrl = `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&units=metric&appid=${key}`
const forcastWeatherResponse = await fetch(forcastWeatherUrl);
if (!forcastWeatherResponse.ok) {
const message = `An error has occured: ${forcastWeatherResponse.status}`;
throw new Error(message);
}
const forcastDataResponse = await forcastWeatherResponse.json();
// Update state with the forcast data
setForcastData(forcastDataResponse);
}
// Call the callForcast function to run
callForcast();
},
// This effect hook is dependent on callWeather
[callWeather])
Now my onClick will only need to call callWeather()
function.
Thanks to:
#Mohammad Arasteh
#Thomas Geenen
#tromgy
I think you should try to call callWeather(); under callForcast() after setForcastData() state set, and if update state value not affected in call weather you can try to add wait in setForcastData().
Or, try to add wait before callForcast() in callWeatherAndForcast() onClick
I have a collection ReferalCodes in which each document is specified has these values where 'mine' value is false 'msg' value is false and mycode is user referal codes.
appliedcode: ""
mine:false
msg:false
mycode:"ASDF4G"
referals : []
And User has one more collection UserTransaction which have a field 'CountTrans' which is initialized as 0 when this value is 1 i want to trigger a function to check value of mine and appliedcode if the value of mine is true then and applied code have value of length 6 then i want to update the UserTransaction of that user and the appliedcode is referal code of some another user want to change UserTransaction of that user also
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const { user } = require('firebase-functions/lib/providers/auth');
admin.initializeApp();
const db = admin.firestore();
exports.getCount= functions.firestore
.document('UserTransactions/{id}')
.onUpdate((change, context) => {
const newTransdata = change.after.data();
const previousTransData = change.before.data();
const docid = context.params.docid;
if(newTransdata.CountTrans === 1) {
const referalData = await
admin.firestore.collection('ReferalCodes').doc(docid).get()
const mine = referalData.mine;
const appliedCode = referalData.appliedCode;
let codes = [];
var myfrindid;
if(mine === true && appliedCode !== null) {
const friendData = await
admin.firestore.collection('ReferalCodes').doc('codes').get();
var referalcodes = friendData['ReferalCodes'];
referalcodes.forEach((items) =>{
if (items['code'] == appliedCode) {
myfrindid = items['id'];
}
});
await
admin.firestore.collection('ReferalCodes').doc(docid).update({
msg: true
})
}else{
}
}
});
Consulting the official documents is explained how to handle events on your database and trigger some different kind of triggers depending on your needs, for example:
onWrite(), which triggers when data is created, updated, or deleted in Realtime Database.
onCreate(), which triggers when new data is created in Realtime Database
onUpdate(), which triggers when data is updated in Realtime Database
onDelete(), which triggers when data is deleted from Realtime Database
For example, the first argument passed your onUpdate handler function is a Change object, this object has two properties, before and after, both DataSnapshot objects. These DataSnapshot objects describe the contents of the database before and after the change that triggered the function.
exports.foo = functions.database.ref('/location-of-interest')
.onUpdate((change) => {
const before = change.before // DataSnapshot before the change
const after = change.after // DataSnapshot after the change
})
I'm trying to pass some values into another screen, it worked the first time when I tried it with one value, using async storage set for a single item, however, now I am trying it with multiple and it keeps crashing every time I press the item I want to get the data from.
Storing the data when I press on an item from a FlatList
fetchOnPressOpacity = async item => {
this.state.totalCalories += item.food.nutrients.ENERC_KCAL;
this.state.totalFat += item.food.nutrients.FAT;
this.state.totalCarbs += item.food.nutrients.CHOCDF;
this.state.totalProtein += item.food.nutrients.PROCNT;
const firstPair = ["totalCalories", JSON.stringify(this.state.totalCalories)];
const secondPair = ["totalCarbs", JSON.stringify(this.state.totalCarbs)];
const thirdPair = ["totalProtein", JSON.stringify(this.state.totalProtein)];
const fourthPair = ["totalFat", JSON.stringify(this.state.totalFat)];
try {
this.setState({});
await AsyncStorage.multiSet(firstPair, secondPair, thirdPair, fourthPair);
} catch (error) {
console.log(error);
}
};
getData() method, I am not too sure how to store the data:
getData = async () => {
try {
const values = await AsyncStorage.multiGet([
"totalCalories",
"totalCarbs",
"totalProtein",
"totalFat"
]);
} catch (e) {
// read error
}
console.log(values);
};
So, right now my main problem is that the application crashes when I press an item.
I get the below error, but do not think this is the issue.
VirtualizedList: missing keys for items, make sure to specify a key or
id property on each item or provide a custom keyExtractor.
I am also able to write to the console the value before the app crashes.
Could you please advise me?
Simple solution
var items = [['key1', 'value1'], ['key2', 'value2']]
AsyncStorage.setItem("DATA_KEY", JSON.stringify(items))
// or
AsyncStorage.multiSet(items, () => {
//to do something
});
For your code
var items = [firstPair, secondPair, thirdPair, fourthPair];
AsyncStorage.setItem("DATA_KEY", JSON.stringify(items))
Get data
AsyncStorage.multiGet(["key1", "key2"]).then(response => {
//to do something
})
Not really a fix to your code but if it's just to pass data to another screen, you could consider to pass data with navigation.
like:
const { navigation } = this.props;
navigation.navigate('YourNextScreen',
{
totalCalories: this.state.totalCalories,
totalCarbs: this.state.totalCarbs,
totalProtein: this.state.totalProtein,
totalFat: this.state.totalFat,
});
and retrieve them in:
const {
totalCalories,
totalCarbs,
totalProtein,
totalFat
} = this.props.route.params;
in case you don't want to specifically save those data for later...
https://reactnavigation.org/docs/params/
I have a firebase function that deletes old messages after 24 hours as in my old question here. I now have just the messageIds stored in an array under the user such that the path is: /User/objectId/myMessages and then an array of all the messageIds under myMessages. All of the messages get deleted after 24 hours, but the iDs under the user's profile stay there. Is there a way to continue the function so that it also deletes the messageIds from the array under the user's account?
I'm new to Firebase functions and javascript so I'm not sure how to do this. All help is appreciated!
Building upon #frank-van-puffelen's accepted answer on the old question, this will now delete the message IDs from their sender's user data as part of the same atomic delete operation without firing off a Cloud Function for every message deleted.
Method 1: Restructure for concurrency
Before being able to use this method, you must restructure how you store entries in /User/someUserId/myMessages to follow best practices for concurrent arrays to the following:
{
"/User/someUserId/myMessages": {
"-Lfq460_5tm6x7dchhOn": true,
"-Lfq483gGzmpB_Jt6Wg5": true,
...
}
}
This allows you to modify the previous function to:
// Cut off time. Child nodes older than this will be deleted.
const CUT_OFF_TIME = 24 * 60 * 60 * 1000; // 2 Hours in milliseconds.
exports.deleteOldMessages = functions.database.ref('/Message/{chatRoomId}').onWrite(async (change) => {
const rootRef = admin.database().ref(); // needed top level reference for multi-path update
const now = Date.now();
const cutoff = (now - CUT_OFF_TIME) / 1000; // convert to seconds
const oldItemsQuery = ref.orderByChild('seconds').endAt(cutoff);
const snapshot = await oldItemsQuery.once('value');
// create a map with all children that need to be removed
const updates = {};
snapshot.forEach(messageSnapshot => {
let senderId = messageSnapshot.child('senderId').val();
updates['Message/' + messageSnapshot.key] = null; // to delete message
updates['User/' + senderId + '/myMessages/' + messageSnapshot.key] = null; // to delete entry in user data
});
// execute all updates in one go and return the result to end the function
return rootRef.update(updates);
});
Method 2: Use an array
Warning: This method falls prey to concurrency issues. If a user was to post a new message during the delete operation, it's ID could be removed while evaluating the deletion. Use method 1 where possible to avoid this.
This method assumes your /User/someUserId/myMessages object looks like this (a plain array):
{
"/User/someUserId/myMessages": {
"0": "-Lfq460_5tm6x7dchhOn",
"1": "-Lfq483gGzmpB_Jt6Wg5",
...
}
}
The leanest, most cost-effective, anti-collision function I can come up for this data structure is the following:
// Cut off time. Child nodes older than this will be deleted.
const CUT_OFF_TIME = 24 * 60 * 60 * 1000; // 2 Hours in milliseconds.
exports.deleteOldMessages = functions.database.ref('/Message/{chatRoomId}').onWrite(async (change) => {
const rootRef = admin.database().ref(); // needed top level reference for multi-path update
const now = Date.now();
const cutoff = (now - CUT_OFF_TIME) / 1000; // convert to seconds
const oldItemsQuery = ref.orderByChild('seconds').endAt(cutoff);
const snapshot = await oldItemsQuery.once('value');
// create a map with all children that need to be removed
const updates = {};
const messagesByUser = {};
snapshot.forEach(messageSnapshot => {
updates['Message/' + messageSnapshot.key] = null; // to delete message
// cache message IDs by user for next step
let senderId = messageSnapshot.child('senderId').val();
if (!messagesByUser[senderId]) { messagesByUser[senderId] = []; }
messagesByUser[senderId].push(messageSnapshot.key);
});
// Get each user's list of message IDs and remove those that were deleted.
let pendingOperations = [];
for (let [senderId, messageIdsToRemove] of Object.entries(messagesByUser)) {
pendingOperations.push(admin.database.ref('User/' + senderId + '/myMessages').once('value')
.then((messageArraySnapshot) => {
let messageIds = messageArraySnapshot.val();
messageIds.filter((id) => !messageIdsToRemove.includes(id));
updates['User/' + senderId + '/myMessages'] = messageIds; // to update array with non-deleted values
}));
}
// wait for each user's new /myMessages value to be added to the pending updates
await Promise.all(pendingOperations);
// execute all updates in one go and return the result to end the function
return ref.update(updates);
});
Update: DO NOT USE THIS ANSWER (I will leave it as it may still be handy for detecting a delete operation for some other need, but do not use for the purpose of cleaning up an array in another document)
Thanks to #samthecodingman for providing an atomic and concurrency safe answer.
If using Firebase Realtime Database you can add an onChange event listener:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.onDeletedMessage = functions.database.ref('Message/{messageId}').onChange(async event => {
// Exit if this item exists... if so it was not deleted!
if (event.data.exists()) {
return;
}
const userId = event.data.userId; //hopefully you have this in the message document
const messageId = event.data.messageId;
//once('value') useful for data that only needs to be loaded once and isn't expected to change frequently or require active listening
const myMessages = await functions.database.ref('/users/' + userId).once('value').snapshot.val().myMessages;
if(!myMessages || !myMessages.length) {
//nothing to do, myMessages array is undefined or empty
return;
}
var index = myMessages.indexOf(messageId);
if (index === -1) {
//nothing to delete, messageId is not in myMessages
return;
}
//removeAt returns the element removed which we do not need
myMessages.removeAt(index);
const vals = {
'myMessages': myMessages;
}
await admin.database.ref('/users/' + userId).update(vals);
});
If using Cloud Firestore can add an event listener on the document being deleted to handle cleanup in your user document:
exports.onDeletedMessage = functions.firestore.document('Message/{messageId}').onDelete(async event => {
const data = event.data();
if (!data) {
return;
}
const userId = data.userId; //hopefully you have this in the message document
const messageId = data.messageId;
//now you can do clean up for the /user/{userId} document like removing the messageId from myMessages property
const userSnapShot = await admin.firestore().collection('users').doc(userId).get().data();
if(!userSnapShot.myMessages || !userSnapShot.myMessages.length) {
//nothing to do, myMessages array is undefined or empty
return;
}
var index = userSnapShot.myMessages.indexOf(messageId);
if (index === -1) {
//nothing to delete, messageId is not in myMessages
return;
}
//removeAt returns the element removed which we do not need
userSnapShot.myMessages.removeAt(index);
const vals = {
'myMessages': userSnapShot.myMessages;
}
//To update some fields of a document without overwriting the entire document, use the update() method
await admin.firestore().collection('users').doc(userId).update(vals);
});
I have been doing this for an hour. I simply want to get the number of children in the child "Success" in the database below. The answers in similar stackoverflow questions are not working. I am new in Javascript Programming.
So far I have tried this
var children = firebase.database().ref('Success/').onWrite(event => {
return event.data.ref.parent.once("value", (snapshot) => {
const count = snapshot.numChildren();
console.log(count);
})
})
and also this
var children = firebase.database().ref('Success/').onWrite(event => {
return event.data.ref.parent.once("value", (snapshot) => {
const count = snapshot.numChildren();
console.log(count);
})
})
Where might I be going wrong.
As explained in the doc, you have to use the numChildren() method, as follows:
var ref = firebase.database().ref("Success");
ref.once("value")
.then(function(snapshot) {
console.log(snapshot.numChildren());
});
If you want to use this method in a Cloud Function, you can do as follows:
exports.children = functions.database
.ref('/Success')
.onWrite((change, context) => {
console.log(change.after.numChildren());
return null;
});
Note that:
The new syntax for Cloud Functions version > 1.0 is used, see https://firebase.google.com/docs/functions/beta-v1-diff?authuser=0
You should not forget to return a promise or a value to indicate to the platform that the Cloud Function execution is completed (for more details on this point, you may watch the 3 videos about "JavaScript Promises" from the Firebase video series: https://firebase.google.com/docs/functions/video-series/).
const db = getDatabase(app)
const questionsRef = ref(db, 'questions')
const mathematicalLiteracy = child(questionsRef, 'mathematicalLiteracy')
onValue(mathematicalLiteracy, (snapshot) => {
const data = snapshot.val()
const lenML = data.length - 1
console.log(lenML)
})
This method worked for me. I wanted to get the children's count of the mathematicalLiteracy node in my database tree. If I get its value using .val() it returns an array that contains that node's children and an extra empty item. So, I subtracted that one empty item's count. Finally, I get my needed children's count.