The below is a sample structure of the database.
root: {
users: {
user_uid_0: {
name: 'AJ',
age: 20,
gender: 'male'
}
}
}
The below is how I am fetching data from Firebase:
getData(myValue) {
// direct ref to a key of 'name' from user_uid_0
const name = firebase.database().ref('/users/user_uid_0/name');
// name will go through a listener
name.on('value', (snapshot) => {
// define myValue to the snapshot
myValue = snapshot.val()
// this.myValue = snapshot.val() tried this...
});
// spit out myValue which we defined
// myValue should be 'AJ'
// but its not... its undefined
return myValue;
// im guessing because myValue is actually not defined...
// then how do I define it with my snapshot.val()?
}
The below is a react native component that is displaying what the function is returning.
// this should return the string 'AJ' from myValue
// this returns nothing
<Text>{this.getData()}</Text>
The below is what I get when I console log.
// undefined is what I get
console.log(this.getData());
So since this wont work, what will? How can I fetch a data through ref and display it? Please help. I have been struggling with this for days.
Try something like this -
getData = async () => {
const name = firebase.database().ref('/users/user_uid_0/name');
var data = await name.once('value')
.then(snapshot => {
console.log("value", snapshot.val());
return snapshot.val();
})
.catch(e => {
console.log("firebase error", e);
return null;
});
return data;
}
Related
I have problem, in the code below I'm trying to console.log only name of the playLists from state with console.log(this.state.playLists.name); I get undefined.
getPlaylist() {
spotifyApi.getUserPlaylists().then((response) => {
if (response.items) {
const items = response.items;
console.log(items);
const playListsId = items.map((obj) => {
const playList = {
name: obj.name,
id: obj.id,
};
return playList;
});
console.log(playListsId);
this.setState({
playLists: playListsId,
});
}
console.log(this.state.playLists.name);
});
}
Since this.state.playLists is an array you cannot get name on it. you have to get the name of all elements in an array.
Try this in place of console.log(this.state.playLists.name);
this.state.platLists.map(playList => console.log(playList.name));
When page loaded first time, I need to get all information, that is why I am calling a fetch request and set results into State [singleCall function doing that work]
Along with that, I am connecting websocket using socket.io and listening to two events (odds_insert_one_two, odds_update_one_two), When I got notify event, I have to
check with previous state and modify some content and update the state without calling again fetch request. But that previous state is still [] (Initial).
How to get that updated state?
Snipptes
const [leagues, setLeagues] = useState([]);
const singleCall = (page = 1, params=null) => {
let path = `${apiPath.getLeaguesMatches}`;
Helper.getData({path, page, params, session}).then(response => {
if(response) {
setLeagues(response.results);
} else {
toast("Something went wrong, please try again");
}
}).catch(err => {
console.log(err);
})
};
const updateData = (record) => {
for(const data of record) {
var {matchId, pivotType, rateOver, rateUnder, rateEqual} = data;
const old_leagues = [...leagues]; // [] becuase of initial state value, that is not updated
const obj_index = old_leagues.findIndex(x => x.match_id == matchId);
if(obj_index > -1) {
old_leagues[obj_index] = { ...old_leagues[obj_index], pivotType, rateOver: rateOver, rateUnder:rateUnder, rateEqual:rateEqual};
setLeagues(old_leagues);
}
}
}
useEffect(() => {
singleCall();
var socket = io.connect('http://localhost:3001', {transports: ['websocket']});
socket.on('connect', () => {
console.log('socket connected:', socket.connected);
});
socket.on('odds_insert_one_two', function (record) {
updateData(record);
});
socket.on('odds_update_one_two', function (record) {
updateData(record);
});
socket.emit('get_odds_one_two');
socket.on('disconnect', function () {
console.log('socket disconnected, reconnecting...');
socket.emit('get_odds_one_two');
});
return () => {
console.log('websocket unmounting!!!!!');
socket.off();
socket.disconnect();
};
}, []);
The useEffect hook is created with an empty dependency array so that it only gets called once, at the initialization stage. Therefore, if league state is updated, its value will never be visible in the updateData() func.
What you can do is assign the league value to a ref, and create a new hook, which will be updated each time.
const leaguesRef = React.useRef(leagues);
React.useEffect(() => {
leaguesRef.current = leagues;
});
Update leagues to leaguesRef.current instead.
const updateData = (record) => {
for(const data of record) {
var {matchId, pivotType, rateOver, rateUnder, rateEqual} = data;
const old_leagues = [...leaguesRef.current]; // [] becuase of initial state value, that is not updated
const obj_index = old_leagues.findIndex(x => x.match_id == matchId);
if(obj_index > -1) {
old_leagues[obj_index] = { ...old_leagues[obj_index], pivotType, rateOver:
rateOver, rateUnder:rateUnder, rateEqual:rateEqual};
setLeagues(old_leagues);
}
}
}
I'm currently trying to get a project working to test some things and I'm stuck at a point where I'm trying to update the state properly.
I have an endpoint accessed via axios.get("/docker/containers") which will return an array for all IDs of the containers which are currently running on my system this is done like so:
componentDidMount() {
this.interval = setInterval(() => this.updateContainers(), 3000);
};
componentWillUnmount() {
clearInterval(this.interval);
}
At this point my state looks like this:
state = {
containers: [{id: 'id1'}, {id: 'id2'}]
}
The user interface then just shows a list of IDs.
I can then click on an ID on my user interface and it will set a watcher:
state = {
containers: [{id: 'id1', watcher: true}, {id: 'id2'}]
}
The point of the watcher is so that on the next update cycle more detailed information about a particular container is retrieved.
state = {
containers: [{id: 'id1', watcher: true, name: 'container1'}, {id: 'id2'}]
}
Upon clicking the container in the user interface where a watcher is already set then the watcher is dropped and the more detailed information is then no longer retrieved
state = {
containers: [{id: 'id1', watcher: false}, {id: 'id2'}]
}
Where I'm getting stuck is on how to get the more detailed information. My updateContainers method has 3 steps:
Read the response from the API and destruct the state into separate variables, compare the state var with the response var and remove any containers that have gone down (no setState is done here).
Add any new containers from the response to the state that have since come up (again no setState).
...All good thus far...
Loop through the filtered array of containers from steps 1 and 2 and find any containers where a watcher is set. Where it is set perform an API call to retrieve the more detailed info. Finally set the state.
In step 3 I use a forEach on the filtered array and then do an axios.get("/docker/containers/id1") where a watcher has been set otherwise simply keep the container details I already have but that's where I get stuck, Typescript is also giving me the error:
TS2322: Type 'void' is not assignable to type 'IndividualContainer[]'.
currently I have:
updateContainers() {
axios.get('/docker/containers')
.then(response => {
const apiRequestedContainers: string[] = response.data.containers;
// array of only IDs
const stateContainers: IndividualContainer[] = [
...this.state.containers
];
// remove dead containers from state by copying still live containers
let filteredContainers: IndividualContainer[] = [
...this.filterOutContainers(stateContainers, apiRequestedContainers)
];
// add new containers
filteredContainers = this.addContainerToArray(
filteredContainers, apiRequestedContainers
);
return this.updateContainer(filteredContainers);
})
.then(finalArray => {
const newState: CState = {'containers': finalArray};
this.setState(newState);
});
};
updateContainer(containers: IndividualContainer[]) {
const returnArray: IndividualContainer[] = [];
containers.forEach(container => {
if (container.watcher) {
axios.get('/docker/containers/' + container.id)
.then(response => {
// read currently available array of containers into an array
const resp = response.data;
resp['id'] = container.id;
resp['watcher'] = true;
returnArray.push(resp);
});
} else {
returnArray.push(container);
}
return returnArray;
});
};
Any pointers to where my logic fails would be appreciated!
Edit:
Render Method:
render() {
const containers: any = [];
const curStateOfContainers: IndividualContainer[] = [...this.state.containers];
if (curStateOfContainers.length > 0) {
curStateOfContainers.map(container => {
const container_id = container.id.slice(0, 12);
containers.push(
<Container
key = {container_id}
container_id = {container.id}
name = {container.name}
clickHandler = {() => this.setWatcher(container.id)}
/>
);
});
}
return containers;
}
I'm not an expert in TypeScript so I had to change the response to JS and thought you'll re-write it in TS in case it's needed.
async updateContainers() {
const response = await axios.get('/docker/containers')
const apiRequestedContainers = response.data.containers; // array of only IDs
const stateContainers = [...this.state.containers];
// remove dead containers from state by copying still live containers
let filteredContainers = [...this.filterOutContainers(stateContainers, apiRequestedContainers)];
// add new containers
filteredContainers = this.addContainerToArray(filteredContainers, apiRequestedContainers);
const containers = await this.updateContainer(filteredContainers)
this.setState({ containers });
};
async updateContainer(containers) {
return containers.map(async (container) => {
if (container.watcher) {
const response = await axios.get('/docker/containers/' + container.id)
// read currently available array of containers into an array
return {
...response.data,
id: container.id,
watcher: true,
}
} else {
return container;
}
});
}
Here's what I've updated in updateContainer:
I'm now mapping the array instead of doing a forEach
I'm now waiting for the container details API to return a value before checking the second container. --> this was the main issue as your code doesn't wait for the API to finish ( await / async )
The problem is that you are returning nothing from updateContainer method which will return void implicitly:
// This function return void
updateContainer(containers: IndividualContainer[]) {
const returnArray: IndividualContainer[] = [];
containers.forEach(container => {
if (container.watcher) {
axios.get("/docker/containers/" + container.id).then(response => {
// read currently available array of containers into an array
const resp = response.data;
resp["id"] = container.id;
resp["watcher"] = true;
returnArray.push(resp);
});
} else {
returnArray.push(container);
}
// this is inside the forEach callback function not updateContainer function
return returnArray;
});
}
Then you assign void to containers which is supposed to be of type IndividualContainer[] so TypeScript gives you an error then you set that in the state:
updateContainers() {
axios
.get("/docker/containers")
.then(response => {
const apiRequestedContainers: string[] = response.data.containers; // array of only IDs
const stateContainers: IndividualContainer[] = [
...this.state.containers
];
// remove dead containers from state by copying still live containers
let filteredContainers: IndividualContainer[] = [
...this.filterOutContainers(stateContainers, apiRequestedContainers)
];
// add new containers
filteredContainers = this.addContainerToArray(
filteredContainers,
apiRequestedContainers
);
// this return void as well
return this.updateContainer(filteredContainers);
})
// finalArray is void
.then(finalArray => {
// you assign void to containers which should be of type IndividualContainer[]
const newState: CState = { containers: finalArray };
// containers will be set to undefined in you state
this.setState(newState);
});
}
You meant to do this:
// I added a return type here so that TypeScript would yell at me if I return void or wrong type
updateContainer(containers: IndividualContainer[]): IndividualContainer[] {
const returnArray: IndividualContainer[] = [];
containers.forEach(container => {
if (container.watcher) {
axios.get("/docker/containers/" + container.id).then(response => {
// read currently available array of containers into an array
const resp = response.data;
resp["id"] = container.id;
resp["watcher"] = true;
returnArray.push(resp);
});
} else {
returnArray.push(container);
}
// removed the return from here as it's useless
});
// you should return the array here
return returnArray;
}
First, I've commented on errors in your code:
updateContainers() {
axios.get('/docker/containers')
.then(response => {
...
return this.updateContainer(filteredContainers);
// returns `undefined`...
})
.then(finalArray => { ... });
// ...so `finalArray` is `undefined` - the reason for TS error
// Also `undefined` is not a `Promise` so this second `then()`
// doesn't make much sense
};
updateContainer(containers: IndividualContainer[]) {
const returnArray: IndividualContainer[] = [];
containers.forEach(container => {
if (container.watcher) {
axios.get('/docker/containers/' + container.id)
.then(response => {
...
returnArray.push(resp)
// because `axios.get()` is asynchronous
// this happens only some time after
// `.then(finalArray => { ... })` is finished
});
// at this moment code inside `.then()` has not been executed yet
// and `resp` has not yet been added to `returnArray`
} else {
returnArray.push(container)
// but this happens while `forEach()` is running
}
return returnArray;
// here you return from `forEach()` not from `updateContainer()`
// also `forEach()` always returns `undefined`
// so even `return containers.forEach(...)` won't work
});
// no return statement, that implicitly means `return undefined`
};
Now, why the #RocKhalil's answer, kind of, works:
async updateContainers() {
const response = await axios.get('/docker/containers')
// he favors a much clearer syntax of async/await
...
const containers = await this.updateContainer(filteredContainers)
this.setState({ containers });
};
async updateContainer(containers) {
return containers.map(async (container) => {
if (container.watcher) {
const response = await axios.get('/docker/containers/' + container.id)
// Because `axios.get()` was **awaited**,
// you can be sure that all code after this line
// executed when the request ended
// while this
// axios.get(...).then(() => console.log(2)); console.log(1)
// will lead to output 1 2, not 2 1
return {
...response.data,
id: container.id,
watcher: true,
}
} else {
return container;
}
});
// he does not forget to return the result of `map()`
// and `map()` in contrast with `forEach()` does have a result
// But...
}
But...
containers.map() returns an array. An array of Promises. Not a single Promise. And that means that
const containers = await this.updateContainer(filteredContainers)
waits for nothing. And updateContainer() function is not actually async.
To fix that you need to use Promise.all():
const containers = await Promise.all(this.updateContainer(filteredContainers))
I have the function below I have implemented and deployed to my cloud functions in my firebase project but upon execution, it causes me an error in the function log of the firebase console;
> TypeError: Cannot read property 'profileImgUrl' of undefined
I understand the meaning of the error it generates, but I don't understand why my user variable will be undefined
Here is the function below:
exports.eventInvites = functions.firestore
.document('Events/{eventId}').onCreate((snap, context) => {
const eventData = snap.data();
const eventId = context.params.eventId;
const invitees = eventData.invitees;
let user;
db.doc(`Users/${eventData.createdBy}`).get().then(doc => {
var data = doc.data();
user = data; // updated user data but still get undefined
return user;
}).catch(error => {
console.log(error.message);
});
if (invitees !== undefined || invitees.length > 0) {
invitees.forEach(invitee => {
db.collection('Users').doc(invitee).collection('feed').doc().set({
date: new Date(),
image: user.profileImgUrl, // error occurs here, says user is undefined
summary: `${user.firstName} ${user.lastName} invited you to an event`,
extraText: `${eventData.eventName}: ${eventData.eventDetails} `,
extraImages: [
eventData.eventImgUrl
]
}).catch(error => {
console.log(error.message)
});
});
}
});
get() is asynchronous therefore the code after it is getting executed before. To solve this, you can put all the code inside get():
db.doc(`Users/${eventData.createdBy}`).get().then(doc => {
var data = doc.data();
user = data;
if (invitees !== undefined || invitees.length > 0) {
invitees.forEach(invitee => {
db.collection('Users').doc(invitee).collection('feed').doc().set({
date: new Date(),
image: user.profileImgUrl,
summary: `${user.firstName} ${user.lastName} invited you to an event`,
extraText: `${eventData.eventName}: ${eventData.eventDetails} `,
extraImages: [
eventData.eventImgUrl
]
})
});
}
return user;
}).catch(error => {
console.log(error.message);
});
I am trying to make an app and I have a few objects in an array which I use AsyncStorage for. I need to change one element of an object but if I try changing it, it doesn't keep those changes.
My code:
save = async () => {
this.state.dataSource.notes = this.state.text;
try {
AsyncStorage.clear();
await AsyncStorage.setItem('medicines', JSON.stringify(this.state.dataSource));
} catch (error) {
console.log('error');
}
this.setModalVisible(!this.state.modalVisible);
The this.state.text stores the value of the text input.
The this.state.dataSource stores the array of objects.
Both of these work like they should.
The array looks like this:
Array = [
item = {
id: 'id',
name: 'name',
brand: 'brand',
inname: 'inname',
chosenWeekDay: 'week day',
androidDate: 'date for android',
chosenAndroidTime: 'time for android',
notes: 'notes in a string',
}]
You need to target a specific object in your dataSource array
let { dataSource, text } = this.state;
const yourSpecfiicIndex = 12; // whatever
dataSource[yourSpecfiicIndex].notes = text;
//...
await AsyncStorage.setItem('medicines', JSON.stringify(dataSource));
I found a solutions:
save = async () => {
this.changeNotes(this.state.id, this.state.text);
try {
AsyncStorage.clear();
await AsyncStorage.setItem('medicines', JSON.stringify(this.state.dataSource));
} catch (error) {
console.log('error');
}
this.setModalVisible(!this.state.modalVisible);
}
changeNotes( value, note ) {
for (var i in this.state.dataSource) {
if (this.state.dataSource[i].id == value) {
this.state.dataSource[i].notes = note;
break;
}
}
}