How to read array of objects with data from fireabse - firebase

I have a function that create tasks and writing it in firebase real time database.
export const createNewTask = (task) => new Promise(async (resolve, reject) => {
try {
await database().ref('tasks').child(auth().currentUser.uid).child(task.taskCreationDate.toString()).set(task);
resolve();
} catch (e) {
reject(e);
}
});
And it's working good :
Now, I have a socond functions that should read that tasks.
export const fetchTasks = () => (dispatch) => new Promise(async (resolve, reject) => {
try {
const snapshot = await database().ref('tasks').child(auth().currentUser.uid).once('value');
if (snapshot.exists) {
const tasks = snapshot.val();
dispatch({
type: FETCH_TASKS,
payload: tasks,
});
resolve(tasks);
} else {
resolve(snapshot);
}
} catch (e) {
reject(e);
}
});
And here is the problem:
When I'm using useSelector in my home screen const tasks = useSelector(state => state.GeneralReducer.taskList);
The tasks list is undefiend.
When I used mock data with same objects , it's work fine.
How can I get a list of task?

Ok , fixed it by adding :
Object.keys(data).map(key => ({...data[key], id: key}));
to the function fetchTasks.
export const fetchTasks = () => (dispatch) => new Promise(async (resolve, reject) => {
try {
const snapshot = await database().ref('tasks').child(auth().currentUser.uid).once('value');
if (snapshot.exists) {
const data = snapshot.val();
const tasks = Object.keys(data).map(key => ({...data[key], id: key}));
dispatch({
type: FETCH_TASKS,
payload: tasks,
});
resolve(tasks);
} else {
resolve(snapshot);
}
} catch (e) {
reject(e);
}
});

Related

RTK Query - update all injectedEndpoints cache with one WebSocket connection (Best Practice)

I am new to RTK query and need only one WebSocket connection for my entire application as you can see below I implemented it like an example in GitHub.
I need to somehow send my payload to this WebSocket by subscribing to it.
and then whenever the message comes in I update the other injected Endpoints' cache.
import { ApiSlice } from 'api';
import { instrumentsAdapter } from './marketSlice';
const socket = new WebSocket(process.env.REACT_APP_SOCKET_BASE_URL);
const socketConnected = new Promise((resolve, reject) => {
// Connection opened
try {
socket.addEventListener('open', (event) => {
resolve(event);
});
} catch (err) {
console.log('err', err);
reject(err);
}
});
export const socketApi = ApiSlice.injectEndpoints({
endpoints: (builder) => ({
socketChannel: builder.mutation({
async queryFn(arg) {
await socketConnected;
const { type, topic } = arg;
const sendPayload = { type, path: topic };
socket.send(JSON.stringify(sendPayload));
return { data: { messages: [] } };
},
async onCacheEntryAdded(arg, { cacheDataLoaded, cacheEntryRemoved }) {
console.log('arg', arg);
await cacheDataLoaded;
// Listen for messages
socket.onmessage = (res) => {
const message = JSON.parse(res.data);
try {
// ApiSlice.util.updateQueryData('getInstrumentByRefId', arg, (draft) => {
// console.log('arg', arg);
// draft = { ...message.value, baseVolume: 3 };
// });
} catch (err) {
console.log('err', err);
}
};
await cacheEntryRemoved;
socket.close();
}
})
})
});
export const { useSocketChannelMutation } = socketApi;
after so much reading docs and researching I finally find this solution working but I do not know if this is a best practice or not.
Here is my not-empty ApiSlice.
/* eslint-disable import/prefer-default-export */
// Or from '#reduxjs/toolkit/query' if not using the auto-generated hooks
import { createApi } from '#reduxjs/toolkit/query/react';
import axiosBaseQuery from './axiosBaseQuery';
export const socket = new WebSocket(process.env.REACT_APP_SOCKET_BASE_URL);
const socketConnected = new Promise((resolve, reject) => {
try {
socket.addEventListener('open', (event) => {
resolve(event);
});
} catch (err) {
reject(err);
}
});
// initialize an empty api service that we'll inject endpoints into later as needed
export const ApiSlice = createApi({
reducerPath: 'api',
baseQuery: axiosBaseQuery(),
endpoints: (builder) => ({
subscribeSocket: builder.mutation({
async queryFn(arg) {
await socketConnected;
const sendPayload = { type: 'SUBSCRIBE', path: arg };
socket.send(JSON.stringify(sendPayload));
return { data: { messages: [] } };
}
}),
unsubscribeSocket: builder.mutation({
async queryFn(arg) {
await socketConnected;
const sendPayload = { type: 'UNSUBSCRIBE', path: arg };
socket.send(JSON.stringify(sendPayload));
return { data: { messages: [] } };
}
}),
channel: builder.mutation({
async queryFn(onMessage) {
await socketConnected;
socket.addEventListener('message', onMessage);
return { data: { messages: [] } };
}
})
})
});
export const { useUnsubscribeSocketMutation, useSubscribeSocketMutation, useChannelMutation } =
ApiSlice;
and this is my enhanced Api slice
import { createEntityAdapter } from '#reduxjs/toolkit';
import { ApiSlice } from 'api';
export const instrumentsAdapter = createEntityAdapter({
selectId: (item) => item?.state?.symbol
});
export const marketApi = ApiSlice.injectEndpoints({
overrideExisting: false,
endpoints: (builder) => ({
getMarketMap: builder.query({
query: (type) => ({
url: `/market/map?type=${type}`,
method: 'get'
})
}),
getInstruments: builder.query({
query: (type) => ({
url: `/market/instruments?type=${type}`,
method: 'get'
})
}),
getInstrumentByRefId: builder.query({
query: (refId) => ({
url: `/market/instruments/${refId}/summary`,
method: 'get'
}),
transformResponse: (res) => {
return instrumentsAdapter.addOne(instrumentsAdapter.getInitialState(), res);
},
async onCacheEntryAdded(
arg,
{ updateCachedData, cacheDataLoaded, cacheEntryRemoved, dispatch }
) {
await cacheDataLoaded;
const payload = `instruments.${arg}.summary`;
// subs to socket
dispatch(ApiSlice.endpoints.subscribeSocket.initiate(payload));
// Listen for messages
const onMessage = (res) => {
const message = JSON.parse(res.data);
try {
updateCachedData((draft) => {
instrumentsAdapter.setOne(draft, message.value);
});
} catch (err) {
// eslint-disable-next-line no-console
console.log('err', err);
}
};
dispatch(ApiSlice.endpoints.channel.initiate(onMessage));
await cacheEntryRemoved;
// unsubs to socket
dispatch(ApiSlice.endpoints.unsubscribeSocket.initiate(payload));
}
}),
getCandles: builder.query({
query: ({ refId, bucket, end, limit = 1 }) => ({
url: `/market/instruments/${refId}/candles?bucket=${bucket}&end=${end}&limit=${limit}`,
method: 'get'
})
})
})
});
export const {
useGetMarketMapQuery,
useGetInstrumentByRefIdQuery,
useGetInstrumentsQuery,
useGetCandlesQuery
} = marketApi;
and I try to dispatch my socket endpoints from ApiSlice inside of onCacheEntryAdded.
async onCacheEntryAdded(
arg,
{ updateCachedData, cacheDataLoaded, cacheEntryRemoved, dispatch }
) {
await cacheDataLoaded;
const payload = `instruments.${arg}.summary`;
// subs to socket
dispatch(ApiSlice.endpoints.subscribeSocket.initiate(payload));
// Listen for messages
const onMessage = (res) => {
const message = JSON.parse(res.data);
try {
updateCachedData((draft) => {
instrumentsAdapter.setOne(draft, message.value);
});
} catch (err) {
// eslint-disable-next-line no-console
console.log('err', err);
}
};
dispatch(ApiSlice.endpoints.channel.initiate(onMessage));
await cacheEntryRemoved;
// unsubs to socket
dispatch(ApiSlice.endpoints.unsubscribeSocket.initiate(payload));
}
}),
```

because my return is [{"singles": [[Object], [Object]]}]

I am using a useEffect to get information from a collection and a sub-collection, however, the data return from my sub-collection is being [{"singles": [[Object], [Object]]}]
useEffect(() => {
fireStore
.collection('users')
// .where('category','==','band')
// .where('status','==','approved')
.where('email','==','marcelomenoli12#gmail.com')
.get()
.then( async (musics) => {
const allSingles = musics.docs.map(async (single) => {
const items = await single.ref.collection('musics').get();
return {
// cover: single.data().cover,
// name: single.data().bandArtistName,
singles: items.docs.map((item) => ({
id: item.id,
...item.data(),
}))
}
});
const filteredSingles = await Promise.all(allSingles);
setDATA(filteredSingles);
});
}, []);
console.log(DATA);
You should use more than one then clauses, so every async operation return values. Otherwise inner await responses are not come on time.
I tried to update your code. I hope that helps.
useEffect(() => {
fireStore
.collection('users')
.where('email','==','marcelomenoli12#gmail.com')
.get()
.then( async (musics) => {
const allSingles = musics.docs.map(async (single) => {
return await single.ref.collection('musics').get();
})
.then((items)=>{
// cover: single.data().cover,
// name: single.data().bandArtistName,
singles: items.docs.map((item) => ({
id: item.id,
...item.data(),
}))
})
}, []);
console.log(DATA);

React native cannot set multiple arrayitems from Firebase in loop

I am trying to get data from Firebase realtime database in the loop and set array items,
but just the last item can set.
it's looking like synchronize problems I tried a lot of things but couldn't solve it.
import FireBaseConnection from '../classes/firebaseconnection.js';
const getComments = () => {
let cardatafetch=[]
FireBaseConnection.GetData('/PostComments/1234').then((comments) => {
for (i in comments) {
cardatafetch.push(comment[i])
}
for (j in cardatafetch) {
var UserId = cardatafetch[j]["UserID"]
FireBaseConnection.GetData('/Users/'+UserId).then((user) => {
cardatafetch[j].ProfilePicture=user["ProfilePicture"]
})
.catch((error) => {
console.log(error)
});
}
console.log(cardatafetch)
}).catch((error) => {
console.log(error)
});
}
}
Console Output is
Same problem also during get images from storage
for (j in cardatafetch) {
FireBaseConnection.GetImage().then((obj) => {
cardatafetch[j].ProfilePicture=obj
})
.catch((error) => {
console.log(error)
});
}
FireBaseConnection Class
import database from '#react-native-firebase/database';
import storage from '#react-native-firebase/storage';
import { utils } from '#react-native-firebase/app';
class FireBaseConnection
{
static async GetData(refValue) {
let data;
await database()
.ref(refValue)
.once('value')
.then(snapshot => {
data = snapshot.val();
});
return data;
}
static async GetImage(imgValue) {
const reference = storage().ref(imgValue);
let imagePath= await reference.getDownloadURL().then(result =>result);
return imagePath;
}
}
export default FireBaseConnection;
Try below code, what I have done is put your code inside last iteration of the loop so it will be implemented only once when all the items are pushed in the array.
import FireBaseConnection from '../classes/firebaseconnection.js';
const getComments = () => {
return new Promise((resolve, reject) => {
let commentsArr = [];
FireBaseConnection.GetData('/PostComments/1234').then((comments) => {
Object.keys(comments).forEach((key, index) => {
commentsArr.push(comments[key])
if(index == Object.keys(comments).length-1) {
resolve(commentsArr);
}
});
}).catch((error) => {
console.log(error)
});
});
}
const addImagesToComment = () => {
this.getComments().then((comments) => {
var finalArr = [];
comments.forEach((comment, index) => {
var tempComment = comment;
var UserId = comment["UserID"]
FireBaseConnection.GetData('/Users/' + UserId).then((user) => {
tempComment.ProfilePicture = user["ProfilePicture"]
finalArr.push(tempComment);
}).catch((error) => {
console.log(error)
});
if(index == comments.length-1) {
console.log(finalArr)
}
});
});
}
Try calling getComments function.

alexa sdk: saving sessionAttributes in https response

i have a skill and i want to load some data from an url and store it in the SessionAttributes.
so i wrote this into my handle(handlerInput)of my LaunchRequestHandler:
require('https').get(url, (resp) => {
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
let attributes = JSON.parse(data);
console.log(attributes);
handlerInput.attributesManager.setSessionAttributes(attributes);
});
});
the log shows me the correct object, but when i try to load the sessionAttributes in the next intent it's empty. I Assume it has something to do with the setSessionAttributes being in the response function, because if i set something directly after this code, it works. Any ideas?
This might be because of the asynchronous operation. Please use async/await to make the API call and then save. Sample example,
const getData = () => {
return new Promise((resolve, reject) => {
require("https").get(url, resp => {
resp.on("data", chunk => {
data += chunk;
});
resp.on("end", () => {
resolve(data);
});
});
});
};
const LaunchRequestHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === "LaunchRequest";
},
async handle(handlerInput) {
const speechText = "welcome";
const data = await getData(); //asynchronous operation
const sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
sessionAttributes.data = data;
handlerInput.attributesManager.setSessionAttributes(sessionAttributes);
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText)
.withSimpleCard("Welcome to the Skill", speechText)
.getResponse();
}
};

Nested promoses, not waiting for the result

I have a getProgrammeWrapper method that returns a promise. However in .then method i have few other promises that i will like to execute before returning the programmeWrapper[]
This is service.ts
I am calling the service in ngOnInit
this.service.getProgrammesByWrapper().then(((res) => {
this.programmes = res;
this.result.updateInfo("Sorting classes...")
this.programmes =this.programmes.sort((a,b) => {
return b.programme.click - a.programme.click;
});
this.result.updateSuccess(true);
}));
I hope i have explained the issue clearly. I have tried using await, but it doesn't work as expected.
getProgrammeWrapper()
getProgrammesByWrapper(): Promise<ProgrammeWrapper[]> {
var current = this;
var programmesDTO = new Array<ProgrammeWrapper>();
var table = this.client.getTable("programme");
return new Promise((resolve, reject) => {
table.read()
.then(function (modules) {
modules.forEach(element => {
var newProgDTO = new ProgrammeWrapper(element);
current.getLessonsByProgrammeId(element.id).then(lessons => newProgDTO.lesson = lessons).catch(err => console.log(err));
current.getUser(element.tutorId).then(user => newProgDTO.tutor = user).catch(err => console.log(err));
programmesDTO.push(newProgDTO)
});
resolve(programmesDTO)
}, function (error) { reject(error) });
});
}
I'm not sure it would work, could have bugs too. Try this:
getProgrammesByWrapper(): Promise<ProgrammeWrapper[]> {
var current = this;
var programmesDTO = new Array<ProgrammeWrapper>();
var table = this.client.getTable("programme");
return new Promise((resolve, reject) => {
table.read()
.then(function (modules) {
let promises = new Array<Promise<any>>();
modules.forEach(element => {
var newProgDTO = new ProgrammeWrapper(element);
let promise = current.getLessonsByProgrammeId(element.id)
.then(lessons => newProgDTO.lesson = lessons)
.catch(err => console.log(err));
promises.push(promise);
promise = current.getUser(element.tutorId)
.then(user => newProgDTO.tutor = user)
.catch(err => console.log(err));
promises.push(promise);
programmesDTO.push(newProgDTO)
});
// Wait for all promises to be ready before resolving
Promise.all(promises).then(function() {
resolve(programmesDTO);
});
}, function (error) { reject(error); });
});
}

Resources