React Native app has low performance while fetching data from firebase - javascript

Hello I have a react native app that gets slow/freeze while fetching data from realtime db.
This is only on android devices, on IOs the app works fine.
for example, by using the performance monitor in my physical device I get:
UI: 59 fps
79 DROPEDD
3 Stuters fo far
JS: -2.1 fps
I get the data in the App.js as the following code, which it is inside an useEffect with an empty dependency array:
async function fetchData() {
try {
const [
postFetched,
categoriesFetched,
assetsFetched,
] = await Promise.all([getAllPosts(), getCategories(), getAssets()]);
const email = await AsyncStorage.getItem('#email');
//Alert.alert(`whats uid ? ${email}`);
if (email) {
const userFetched = await searchUserByEmail(email);
setUser(Object.values(userFetched.val())[0]);
}
// eslint-disable-next-line no-undef
const t2 = performance.now();
//Alert.alert(`time ${(t2 - t1) / 1000} seg`);
return {postFetched, categoriesFetched, assetsFetched};
} catch (error) {
throw new Error(error.toString());
}
}
fetchData()
.then((data) => {
const {postFetched, categoriesFetched, assetsFetched} = data;
if (postFetched.exists()) {
setPosts(
orderBy(
toArray(postFetched.val()),
['isTop', 'created'],
['desc', 'desc'],
),
);
//setLoading(false);
}
if (categoriesFetched.exists()) {
setCategories(toArray(categoriesFetched.val()));
}
if (assetsFetched.exists()) {
setAssetsFirebase(assetsFetched.val());
// SplashScreenNative.hide();
}
// SplashScreenNative.hide();
/*if (Platform.OS === 'android') {
setIsLoading(false);
}*/
})
.catch((error) => {
Toast.show({
type: 'error',
text1: 'error ' + error.toString(),
});
console.log('error', error);
});
listenPostsChange(); // -> this also inside the same use effect
the listenPostChange function:
const listenPostsChange = () => {
listenPosts((data) => {
data.exists() &&
setPosts(
orderBy(toArray(data.val()), ['isTop', 'created'], ['desc', 'desc']),
);
});
};
the firebase methods are defined in a separate js file for each reference:
post.js
import database from '#react-native-firebase/database';
const PostsRef = database().ref('/posts');
export const listenPosts = (callbackFunction) => {
return PostsRef.on('value', callbackFunction);
};
export const listenPosts = (callbackFunction) => {
return PostsRef.on('value', callbackFunction);
};
category.js
import database from '#react-native-firebase/database';
const CategoriesRef = database().ref('/categories');
export const addCategory = (label, category) => {
return CategoriesRef.child(label).update({...category});
};
export const getCategories = () => {
return CategoriesRef.once('value', (snapshot) =>
snapshot.exists() ? snapshot.val() : [],
);
};
assets.js
import database from '#react-native-firebase/database';
const AssetsRef = database().ref('/assets');
export const getAssets = () => {
return AssetsRef.once('value', (snapshot) =>
snapshot.exists() ? snapshot.val() : [],
);
};
I have the following dependencies installed in my app:
"#react-native-firebase/app": "^14.2.2",
"#react-native-firebase/auth": "^14.2.2",
"#react-native-firebase/database": "^14.11.0",
"#react-native-firebase/storage": "^14.11.0",
"firebase": "8.10.1",
I think the main problem is related to firebase since when I deleted the fetchData and the listenPostsChange functions, the app worked smoothly but without data to show.
I hope someone can help me
thanks.
edit: I just have like 15 post and 6 categories, and the assets are just 3 strings values.

Related

error in firebase function, Firebase: No Firebase App '[DEFAULT]' has been created

I'm trying to interact with the database in a function, but I'm getting an error when trying to use the doc() function within a firebase function
The error im getting is: " Firebase: No Firebase App '[DEFAULT]' has been created - call Firebase App.initializeApp() (app/no-app).
"
See the code below and the function I'm getting the error on.
Im guessing in has something to do with not being able to use the db variable? But if I cant do that, how do I interact with the database?
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
import {
collectionGroup, doc, getDoc,
getDocs,
getFirestore,
query,
} from "firebase/firestore";
// Sendgrid Config
import sgMail from "#sendgrid/mail";
import {
ClientUserData,
Course,
EventType,
Task,
} from "../../src/views/types/interfaces";
import {startOfDay} from "date-fns";
const adminApp = admin.initializeApp();
const auth = adminApp.auth();
const db = getFirestore();
//THE FUNCTION IT FAILS ON
export const checkCourseForNewClientProgram = functions.pubsub.schedule("Every day").onRun(async () => {
const courses: Course[] = [];
const coursesByCompanies = query(collectionGroup(db, "courses"));
// eslint-disable-next-line max-len
const checkCourseForNewClientProgramSnapshot = await getDocs(coursesByCompanies);
checkCourseForNewClientProgramSnapshot.forEach((courseSnapshot) => {
const course = courseSnapshot.data() as Course;
if (course.events && course.events.length > 0) {
const courseEvents = course.events;
const todayDate = startOfDay(new Date()).getTime();
courseEvents.forEach((event) => {
// eslint-disable-next-line #typescript-eslint/ban-ts-comment
// #ts-ignore
// eslint-disable-next-line max-len
const eventStart = convertFirebaseDateIntoJSDate(event.start.seconds).getTime();
if (todayDate === eventStart) {
courses.push(course);
}
if (event.type === EventType.TASK) {
const eventLessons = (event as Task).lessons;
if (eventLessons && eventLessons.length > 0) {
eventLessons.forEach((eventLesson) => {
// eslint-disable-next-line #typescript-eslint/ban-ts-comment
// #ts-ignore
// eslint-disable-next-line max-len
const lessonStart = convertFirebaseDateIntoJSDate(eventLesson.start.seconds).getTime();
if (todayDate === lessonStart) {
courses.push(course);
}
});
}
}
});
}
});
let userIds: string[] = [];
courses.forEach((course) => {
if (course.users) {
userIds = course.users.map((userId) => userId);
}
});
const userEmails = await Promise.all(
userIds.map(async (userId) => {
// eslint-disable-next-line max-len
const userData = await getDocumentFromId<ClientUserData>("company_clients", userId);
return userData.email;
})
);
await Promise.all(
userEmails.map(async (userEmail) => {
const msg = {
to: userEmail,
from: "nicky#byplayart.dk",
templateId: "d-8609d087e96e42d5abe6991d19afb22d",
dynamic_template_data: {
email: userEmail,
toName: "Testing tasks",
fromName: "Unlimited Performance",
subject: "Du har en opgave der venter",
text: "Dette er din tekst",
},
};
try {
await sgMail.send(msg);
return {
success: true,
};
} catch (e) {
return {
success: false,
error: e,
};
}
})
);
});
With the Admin SDK version 9 you should use the namespaced syntax, similarly to the Client SDK v8. The corresponding reference for the Admin DSK is actually the Node.js one.
Your code should then been adapted as follows:
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
admin.initializeApp();
const db = admin.firestore();
export const checkCourseForNewClientProgram = functions.pubsub.schedule("Every day").onRun(async () => {});
const coursesByCompanies = db.collectionGroup("courses");
const checkCourseForNewClientProgramSnapshot = await coursesByCompanies.get();
checkCourseForNewClientProgramSnapshot.forEach((courseSnapshot) => {...});
//...
return null;
});
Don't forget to correctly terminate your Cloud Function, either by returning the Promise returned by the last Promise.all() call or a value (e.g. return null;) after all the asynchronous work is complete. This is a key point and I would suggest you read this part of the doc and watch the 3 videos about "JavaScript Promises" from the Firebase video series.

When routing mswjs/data populates the database with new items and removes the previous one, making it inaccessible

I use next-redux-wrapper, MSW, #mswjs/data and redux-toolkit for storing my data in a store as well as mocking API calls and fetching from a mock Database.
I have the following scenario happening to me.
I am on page /content/editor and in the console and terminal, I can see the data was fetched from the mock database and hydrated from getStaticProps of Editor.js. So now IDs 1 to 6 are inside the store accessible.
Now I click on the PLUS icon to create a new project. I fill out the dialog and press "SAVE". a POST request starts, it's pending and then it gets fulfilled. The new project is now in the mock DB as well as in the store, I can see IDs 1 to 7 now.
Since I clicked "SAVE" and the POST request was successful, I am being routed to /content/editor/7 to view the newly created project.
Now I am on Page [id].js, which also fetched data from the mock DB and then it gets stored and hydrated into the redux store. The idea is, it takes the previous store's state and spreads it into the store, with the new data (if there are any).
Now the ID 7 no longer exists. And IDs 1 to 6 also don't exist anymore, instead, I can see in the console and terminal that IDs 8 to 13 were created, and the previous ones are no more.
Obviously, this is not great. When I create a new project and then switch the route, I should be able to access the newly created project as well as the previously created ones. But instead, they all get overwritten.
It either has something to do with the next-redux-wrapper or MSW, but I am not sure how to make it work. I need help with it. I will post some code now:
Code
getStaticProps
// path example: /content/editor
// Editor.js
export const getStaticProps = wrapper.getStaticProps(
(store) =>
async ({ locale }) => {
const [translation] = await Promise.all([
serverSideTranslations(locale, ['editor', 'common', 'thesis']),
store.dispatch(fetchProjects()),
store.dispatch(fetchBuildingBlocks()),
]);
return {
props: {
...translation,
},
};
}
);
// path example: /content/editor/2
// [id].js
export const getStaticProps = wrapper.getStaticProps(
(store) =>
async ({ locale, params }) => {
const { id } = params;
const [translation] = await Promise.all([
serverSideTranslations(locale, ['editor', 'common', 'thesis']),
store.dispatch(fetchProjects()),
// store.dispatch(fetchProjectById(id)), // issue: fetching by ID returns null
store.dispatch(fetchBuildingBlocks()),
]);
return {
props: {
...translation,
id,
},
};
}
);
Mock Database
Factory
I am going to shorten the code to the relevant bits. I will remove properties for a project, as well es helper functions to generate data.
const asscendingId = (() => {
let id = 1;
return () => id++;
})();
const isDevelopment =
process.env.NODE_ENV === 'development' || process.env.STORYBOOK || false;
export const projectFactory = () => {
return {
id: primaryKey(isDevelopment ? asscendingId : nanoid),
name: String,
// ... other properties
}
};
export const createProject = (data) => {
return {
name: data.name,
createdAt: getUnixTime(new Date()),
...data,
};
};
/**
* Create initial set of tasks
*/
export function generateMockProjects(amount) {
const projects = [];
for (let i = amount; i >= 0; i--) {
const project = createProject({
name: faker.lorem.sentence(faker.datatype.number({ min: 1, max: 5 })),
dueDate: date(),
fontFamily: getRandomFontFamily(),
pageMargins: getRandomPageMargins(),
textAlign: getRandomTextAlign(),
pageNumberPosition: getRandomPageNumberPosition(),
...createWordsCounter(),
});
projects.push(project);
}
return projects;
}
API Handler
I will shorten this one to GET and POST requests only.
import { db } from '../../db';
export const projectsHandlers = (delay = 0) => {
return [
rest.get('https://my.backend/mock/projects', getAllProjects(delay)),
rest.get('https://my.backend/mock/projects/:id', getProjectById(delay)),
rest.get('https://my.backend/mock/projectsNames', getProjectsNames(delay)),
rest.get(
'https://my.backend/mock/projects/name/:id',
getProjectsNamesById(delay)
),
rest.post('https://my.backend/mock/projects', postProject(delay)),
rest.patch(
'https://my.backend/mock/projects/:id',
updateProjectById(delay)
),
];
};
function getAllProjects(delay) {
return (request, response, context) => {
const projects = db.project.getAll();
return response(context.delay(delay), context.json(projects));
};
}
function postProject(delay) {
return (request, response, context) => {
const { body } = request;
if (body.content === 'error') {
return response(
context.delay(delay),
context.status(500),
context.json('Server error saving this project')
);
}
const now = getUnixTime(new Date());
const project = db.project.create({
...body,
createdAt: now,
maxWords: 10_000,
minWords: 7000,
targetWords: 8500,
potentialWords: 1500,
currentWords: 0,
});
return response(context.delay(delay), context.json(project));
};
}
// all handlers
import { buildingBlocksHandlers } from './api/buildingblocks';
import { checklistHandlers } from './api/checklist';
import { paragraphsHandlers } from './api/paragraphs';
import { projectsHandlers } from './api/projects';
import { tasksHandlers } from './api/tasks';
const ARTIFICIAL_DELAY_MS = 2000;
export const handlers = [
...tasksHandlers(ARTIFICIAL_DELAY_MS),
...checklistHandlers(ARTIFICIAL_DELAY_MS),
...projectsHandlers(ARTIFICIAL_DELAY_MS),
...buildingBlocksHandlers(ARTIFICIAL_DELAY_MS),
...paragraphsHandlers(ARTIFICIAL_DELAY_MS),
];
// database
import { factory } from '#mswjs/data';
import {
buildingBlockFactory,
generateMockBuildingBlocks,
} from './factory/buildingblocks.factory';
import {
checklistFactory,
generateMockChecklist,
} from './factory/checklist.factory';
import { paragraphFactory } from './factory/paragraph.factory';
import {
projectFactory,
generateMockProjects,
} from './factory/project.factory';
import { taskFactory, generateMockTasks } from './factory/task.factory';
export const db = factory({
task: taskFactory(),
checklist: checklistFactory(),
project: projectFactory(),
buildingBlock: buildingBlockFactory(),
paragraph: paragraphFactory(),
});
generateMockProjects(5).map((project) => db.project.create(project));
const projectIds = db.project.getAll().map((project) => project.id);
generateMockTasks(20, projectIds).map((task) => db.task.create(task));
generateMockBuildingBlocks(10, projectIds).map((block) =>
db.buildingBlock.create(block)
);
const taskIds = db.task.getAll().map((task) => task.id);
generateMockChecklist(20, taskIds).map((item) => db.checklist.create(item));
Project Slice
I will shorten this one as well to the relevant snippets.
// projects.slice.js
import {
createAsyncThunk,
createEntityAdapter,
createSelector,
createSlice,
current,
} from '#reduxjs/toolkit';
import { client } from 'mocks/client';
import { HYDRATE } from 'next-redux-wrapper';
const projectsAdapter = createEntityAdapter();
const initialState = projectsAdapter.getInitialState({
status: 'idle',
filter: { type: null, value: null },
statuses: {},
});
export const fetchProjects = createAsyncThunk(
'projects/fetchProjects',
async () => {
const response = await client.get('https://my.backend/mock/projects');
return response.data;
}
);
export const saveNewProject = createAsyncThunk(
'projects/saveNewProject',
async (data) => {
const response = await client.post('https://my.backend/mock/projects', {
...data,
});
return response.data;
}
);
export const projectSlice = createSlice({
name: 'projects',
initialState,
reducers: {
// irrelevant reducers....
},
extraReducers: (builder) => {
builder
.addCase(HYDRATE, (state, action) => {
// eslint-disable-next-line no-console
console.log('HYDRATE', action.payload);
const statuses = Object.fromEntries(
action.payload.projects.ids.map((id) => [id, 'idle'])
);
return {
...state,
...action.payload.projects,
statuses,
};
})
.addCase(fetchProjects.pending, (state, action) => {
state.status = 'loading';
})
.addCase(fetchProjects.fulfilled, (state, action) => {
projectsAdapter.addMany(state, action.payload);
state.status = 'idle';
action.payload.forEach((item) => {
state.statuses[item.id] = 'idle';
});
})
.addCase(saveNewProject.pending, (state, action) => {
console.log('SAVE NEW PROJECT PENDING', action);
})
.addCase(saveNewProject.fulfilled, (state, action) => {
projectsAdapter.addOne(state, action.payload);
console.group('SAVE NEW PROJECT FULFILLED');
console.log(current(state));
console.log(action);
console.groupEnd();
state.statuses[action.payload.id] = 'idle';
})
// other irrelevant reducers...
},
});
This should be all the relevant code. If you have questions, please ask them and I will try to answer them.
I have changed how the state gets hydrated, so I turned this code:
.addCase(HYDRATE, (state, action) => {
// eslint-disable-next-line no-console
console.log('HYDRATE', action.payload);
const statuses = Object.fromEntries(
action.payload.projects.ids.map((id) => [id, 'idle'])
);
return {
...state,
...action.payload.projects,
statuses,
};
})
Into this code:
.addCase(HYDRATE, (state, action) => {
// eslint-disable-next-line no-console
console.group('HYDRATE', action.payload);
const statuses = Object.fromEntries(
action.payload.projects.ids.map((id) => [id, 'idle'])
);
state.statuses = { ...state.statuses, ...statuses };
projectsAdapter.upsertMany(state, action.payload.projects.entities);
})
I used the adapter to upsert all entries.

Why is my UseEffect for making requests to Spotify API giving an error 429?

I am currently making a Spotify clone which gives user a preview of the song. The problem occurs when I am making many different api requests. When there are more than one requests on the page, it throws a 429 error(making too many requests at once).
Please read through the whole question as I have mentioned the steps I have taken to fix this below.
Profile.js
const { api, refreshableCall } = useSpotify()
const [error, setError] = useState(null)
const [userName, setUserName] = useState("")
const [userFollowers, setUserFollowers] = useState("")
const [userImage, setUserImage] = useState([])
const [userLink, setUserLink] = useState("")
const [userId, setUserId] = useState("")
const [userFollowing, setUserFollowing] = useState("")
const [userTopArtists, setUserTopArtists] = useState([])
const [userTopSongs, setUserTopSongs] = useState([])
useEffect(() => {
let disposed = false
refreshableCall(() => api.getMyTopTracks({
limit: 10,
time_range: "long_term"
}))
.then((res) => {
if (disposed) return
setUserTopSongs(res.body.items)
setError(null)
})
.catch((err) => {
if (disposed) return
setUserTopSongs([])
setError(err)
});
return () => disposed = true
})
useEffect(() => {
let disposed = false
refreshableCall(() => api.getMe())
.then((res) => {
if (disposed) return
var data = res.body
setUserName(data.display_name)
setUserImage(data.images)
setUserFollowers(data.followers["total"])
setUserLink(data.external_urls.spotify)
setUserId(data.id)
setError(null)
})
.catch((err) => {
if (disposed) return
setUserName("")
setUserImage([])
setUserFollowers("")
setUserLink("")
setUserId("")
setError(err)
});
return () => disposed = true
})
useEffect(() => {
let disposed = false
refreshableCall(() => api.getFollowedArtists())
.then((res) => {
if (disposed) return
var data = res.body
var artists = data.artists
setUserFollowing(artists.total)
})
.catch((err) => {
if (disposed) return
setUserFollowing([])
setError(err)
});
return () => disposed = true
})
useEffect(() => {
let disposed = false
refreshableCall(() => api.getMyTopArtists({
limit: 10,
time_range: "long_term"
}))
.then((res) => {
if (disposed) return
var data = res.body
var artists = data.items
setUserTopArtists(artists)
setError(null)
})
.catch((err) => {
if (disposed) return
setUserTopArtists([])
setError(err)
});
return () => disposed = true
})
SpotifyContext.js
import React, { useState, useEffect, useContext } from "react"
import axios from "axios"
import SpotifyWebApi from 'spotify-web-api-node';
const spotifyApi = new SpotifyWebApi({
clientId: 1234567890,
});
export const SpotifyAuthContext = React.createContext({
exchangeCode: () => { throw new Error("context not loaded") },
refreshAccessToken: () => { throw new Error("context not loaded") },
hasToken: spotifyApi.getAccessToken() !== undefined,
api: spotifyApi
});
export const useSpotify = () => useContext(SpotifyAuthContext);
function setStoredJSON(id, obj) {
localStorage.setItem(id, JSON.stringify(obj));
}
function getStoredJSON(id, fallbackValue = null) {
const storedValue = localStorage.getItem(id);
return storedValue === null
? fallbackValue
: JSON.parse(storedValue);
}
export function SpotifyAuthContextProvider({ children }) {
const [tokenInfo, setTokenInfo] = useState(() => getStoredJSON('myApp:spotify', null))
const hasToken = tokenInfo !== null
useEffect(() => {
if (tokenInfo === null) return;
// attach tokens to `SpotifyWebApi` instance
spotifyApi.setCredentials({
accessToken: tokenInfo.accessToken,
refreshToken: tokenInfo.refreshToken,
})
// persist tokens
setStoredJSON('myApp:spotify', tokenInfo)
}, [tokenInfo])
function exchangeCode(code) {
return axios
.post("http://localhost:3001/login", {
code
})
.then(res => {
// TODO: Confirm whether response contains `accessToken` or `access_token`
const { accessToken, refreshToken, expiresIn } = res.data;
// store expiry time instead of expires in
setTokenInfo({
accessToken,
refreshToken,
expiresAt: Date.now() + (expiresIn * 1000)
});
})
}
function refreshAccessToken() {
const refreshToken = tokenInfo.refreshToken;
return axios
.post("http://localhost:3001/refresh", {
refreshToken
})
.then(res => {
const refreshedTokenInfo = {
accessToken: res.data.accessToken,
// some refreshes may include a new refresh token!
refreshToken: res.data.refreshToken || tokenInfo.refreshToken,
// store expiry time instead of expires in
expiresAt: Date.now() + (res.data.expiresIn * 1000)
}
setTokenInfo(refreshedTokenInfo)
// attach tokens to `SpotifyWebApi` instance
spotifyApi.setCredentials({
accessToken: refreshedTokenInfo.accessToken,
refreshToken: refreshedTokenInfo.refreshToken,
})
return refreshedTokenInfo
})
}
async function refreshableCall(callApiFunc) {
if (Date.now() > tokenInfo.expiresAt)
await refreshAccessToken();
try {
return await callApiFunc()
} catch (err) {
if (err.name !== "WebapiAuthenticationError")
throw err; // rethrow irrelevant errors
}
// if here, has an authentication error, try refreshing now
return refreshAccessToken()
.then(callApiFunc)
}
return (
<SpotifyAuthContext.Provider value={{
api: spotifyApi,
exchangeCode,
hasToken,
refreshableCall,
refreshAccessToken
}}>
{children}
</SpotifyAuthContext.Provider>
)
}
Errors
Without the dependency, it keeps cycling and firing off requests, likely hundreds per second. (Error 429)
With the dependency, it seems the Access Token is being ignored or sidestepped. (Error: WebApiAuthentication - No token provided)
What I have tried to do ?
I tried to implement all the requests in a single useEffect, still getting the errors.
Calling useEffect with dependency array and without.
Link to the Github Repo
https://github.com/amoghkapoor/spotify-clone
status 429 means you have made too many calls in a specific time window.
you are therefore banned for this specific time window.
try waiting a bit before retrying.
did you try :
useEffect(..., [])
this guaranties it will be run only once.
None of your useEffect calls are using a dependency array, remember if useEffect is called without any dependencies it goes into an infinite loop. Either find what dependency or state change should re-run the useEffect hook and include it in the dependency array:
useEffect(() => { /* your logic */ }, [dependencies])
or if there are no dependencies simply fire it once the component mounts:
useEffect(() => { /* your logic */ }, [])

VueJS Component failing to render when fetching data

I'm new to Vue.JS and JavaScript, so I have awful times debugging these applications, specially with promises and asynchronous tools. I'm trying to build my first Vue component that fetches data from somewhere. I'm using the Google Sheets API and returning some cells of a sample sheet. My component looks like this:
<template>
<ul>
<li v-for="athlete in athletes" :key="athlete">
{{ athlete }}
</li>
</ul>
</template>
<script>
import readCopaPinheiros from '#/sheets/fetchData.js';
export default {
name: 'AthletesTable',
data () {
return {
loading: false,
athletes: null
}
},
created () {
this.fetchData()
},
methods: {
fetchData() {
this.loading = true;
readCopaPinheiros('inscritos').then(values => {
this.loading = false;
console.log(values)
this.athletes = values
});
},
}
}
</script>
<style>
</style>
EDIT 1
The fetchData script:
const fs = require('fs');
const { google } = require('googleapis');
const TOKEN_PATH = '';
const CREDENTIALS_PATH = ''
const credentials = JSON.parse(fs.readFileSync(CREDENTIALS_PATH, 'utf-8'));
const {
client_secret: clientSecret,
client_id: clientId,
redirect_uris: redirectUris,
} = credentials.installed;
const oAuth2Client = new google.auth.OAuth2(
clientId, clientSecret, redirectUris[0],
);
const token = fs.readFileSync(TOKEN_PATH, 'utf-8');
oAuth2Client.setCredentials(JSON.parse(token));
async function readSheet(spreadsheetId, range) {
const sheets = google.sheets({ version: 'v4', auth: oAuth2Client });
return sheets.spreadsheets.values.get({
spreadsheetId,
range,
})
.then(res => res.data.values)
.catch(err => console.log('Opa! Erro:', err));
}
function readSheetJsnofied(spreadsheetId, range) {
return readSheet(spreadsheetId, range)
.then(values => jsonifySheet(values));
}
function jsonifySheet(sheetValues) {
const header = sheetValues[0];
const values = sheetValues.slice(1);
return values.map((row) => {
const rowObj = ({});
for (let i=0; i < row.length; i++) rowObj[header[i]] = row[i];
return rowObj;
});
}
const readCopaPinheiros = d => readSheetJsnofied('sheetId', d);
export default readCopaPinheiros
For some reason the component doesn't render. I don't know what to do even to debug, all my console log tries never prints something to the console. Could someone help me understand what is going wrong?
EDIT 2
This error just shows up when trying to fetch data:
When I try to use a placeholder list with fake values directly in the data function it works. I don't believe that is a problem with the rendering itself, but how it interacts with the created and fetchData functions.
v-for="athlete in athletes"
This code only works when the athletes is an array. Initially, you set it as null so until the data from api is arrived, it will be null.
But the component still tries to render the component with your null athletes and will make the error.
You can try with this solution:
data () {
return {
loading: false,
athletes: []
}
},

What is the correct way to do an async action using redux observable and firebase?

so I have this epic
export const listenToCountryVisitsEpic = (action$, store) => {
return action$.pipe(
ofType('UPDATE_TRIP_DETAILS'),
mergeMap((action) => {
const userId = store.value.user.id
const { country, newDetails } = action
const { people, places } = details
const scheme = {
people,
places,
}
firebase.database().ref(`users/${userId}/path`).update(scheme)
return mergeMap((response) => {
const payload = { ...JSON.parse(response.request.body), currentDetails: action.currentDetails }
return [updatedCountrySuccess(payload)]
})
}),
)
}
what I want to do is make the request to firebase and then if successful update redux which in turn will update react. if not successful then catch it
this is correctly posting to firebase but I need to catch errors correctly. I don't want to fire this: updatedCountrySuccess until I know it's a success or failure in firebase
can I use async/await for this?
You can use from() to turn Promise into Observable and then catchError to replace the error with whatever you want.
import { from } from 'rxjs';
import { catchError } from 'rxjs/operators';
...
return action$.pipe(
ofType('UPDATE_TRIP_DETAILS'),
mergeMap((action) => {
const userId = store.value.user.id
const { country, newDetails } = action
const { people, places } = details
const scheme = {
people,
places,
}
return from(firebase.database().ref(`users/${userId}/path`).update(scheme))
.pipe(
// `mergeMap` will handle only `next` notifications
mergeMap((response) => {
const payload = ...
return [updatedCountrySuccess(payload)]
}),
catchError(error => of(errorAction)),
);
}),
)

Categories

Resources