getting error while firebase version 9 FCM implementation in Next 12 - javascript

I wanted to add push notification on my next js(version 12) app. so implemented firebase Cloud messaging on that. implementation looks like this :
import { initializeApp, getApp, getApps } from "firebase/app"
import { getMessaging, getToken } from "firebase/messaging"
import { firebaseConfig } from "./config"
const app = !getApps.length ? initializeApp(firebaseConfig) : getApp()
and added this cloudMessaging function for getting FCM token. and onMessageListener function for displaying foreground messages
export const cloudMessaging = async () => {
const token = await isTokenAvailable()
if (token !== null) {
return Promise.resolve({
status: true,
token: token,
})
}
try {
const permission = await Notification.requestPermission()
const messaging = getMessaging(app)
console.log(messaging)
console.log(permission)
if (permission === "granted") {
const FCM_TOKEN = await getToken(messaging, {
vapidKey: process.env.NEXT_PUBLIC_FCM_VAPID_KEY,
})
if (FCM_TOKEN) {
localStorage.setItem("fcm_token_prac", FCM_TOKEN)
return Promise.resolve({
status: true,
token: FCM_TOKEN,
})
}
}
} catch (err) {
console.log(err, "cloudmessaging error")
return Promise.resolve({
status: false,
})
}
}
export const onMessageListener = () => {
const messaging = getMessaging(app)
console.log(messaging)
return new Promise((res) => {
messaging.onMessage((payload) => {
res(payload)
})
})
}
And invoking these function from my Layout component
useEffect(() => {
firebaseInit()
async function firebaseInit() {
try {
await cloudMessaging()
} catch (err) {
console.log(err)
}
}
}, [])
useEffect(() => {
onMessageListener()
.then((payload) => {
console.log(payload, "onMessageListener")
})
.catch((err) => console.log(err, "onMessageListener useEffect"))
}, [])
But getting this error in my console :
TypeError: messaging.onMessage is not a function
at eval (firbase.js?100f:58:15)
at new Promise (<anonymous>)
at onMessageListener (firbase.js?100f:57:10)
at eval (Layout.js?4f8d:33:22)
at commitHookEffectListMount (react-dom.development.js?ac89:23049:1)
I'm unable to find out where I made the mistake. Can anyone guide me to implement this?

I think you can try adding onMessage in the import part.
import { getMessaging, getToken, onMessage } from "firebase/messaging"
Remove messaging on line
messaging.onMessage
becomes
onMessage((payload) => {
res(payload)
})
This is for SDK version 9

Related

About getting data from RTK Query to Firebase Realtime Database

I am currently trying to get data from a Firebase Realtime Database using RTK Query. However, the code here is giving me an error because the value returned in return is not correct. If anyone has knowledge of this, I would appreciate it if you could correct the code to the correct one.
import { createApi, fakeBaseQuery } from "#reduxjs/toolkit/query/react";
import { onValue, ref } from "firebase/database";
import { db } from "libs/firebase";
export const userApi = createApi({
baseQuery: fakeBaseQuery(),
endpoints: builder => ({
getUser: builder.query({
queryFn(uid) {
try {
onValue(ref(db, `users/user${uid}`), snapshot => {
return { data: snapshot.val() };
});
} catch (e) {
return { error: e };
}
},
}),
}),
});
export const { useGetUserQuery } = userApi;
import { configureStore } from "#reduxjs/toolkit";
import { userApi } from "./apiSlice";
export const store = configureStore({
reducer: {
[userApi.reducerPath]: userApi.reducer,
},
middleware: getDefaultMiddleware =>
getDefaultMiddleware().concat(userApi.middleware),
});
const { data: user, error, isLoading, isSuccess } = useGetUserQuery(1);
console.log(user);
error message
Try something along the lines of
export const userApi = createApi({
baseQuery: fakeBaseQuery(),
endpoints: (builder) => ({
getUser: builder.query({
async queryFn(uid) {
try {
const data = await new Promise((resolve, reject) =>
onValue(
ref(db, `users/user${uid}`),
(snapshot) => resolve(snapshot.toJSON()),
reject
)
);
return { data };
} catch (e) {
return { error: e };
}
},
}),
}),
});

The 'refreshToken' function makes the dependencies of useEffect Hook (at line 142) change on every render

I've been trying to deploy my first React App and i cannot seem to be able to get rid of the following warning which on production stops me from doing a deployment:
Line 60:11: The 'refreshToken' function makes the dependencies of useEffect Hook (at line 142) change on every render. Move it inside the useEffect callback. Alternatively, wrap the definition of 'refreshToken' in its own useCallback() Hook react-hooks/exhaustive-deps
Is there an easy way to fix this without breaking the JWT token authentification?
AuthContext.js
import React, { useEffect, useState, useCallback } from 'react'
import { API } from "../api"
import axios from "axios"
import { isAfter, isEqual, parseISO, sub } from 'date-fns'
export const AuthContext = React.createContext(null)
export function AuthContextProvider({ children }) {
const [accessTokenExpiration, setAccessTokenExpiraton] = useState(undefined);
const getUser = () => {
return JSON.parse(localStorage.getItem('user'))
}
const isLoggedIn = () => {
return localStorage.getItem('user') !== null
}
const [user, setUser] = useState(() => {
return isLoggedIn() ? getUser() : null;
})
const [shouldGoToLogin, setShouldGoToLogin] = useState(() => {
if (!user || !user.access_token || !user.refresh_token) {
return true;
}
return false;
})
const logout = async () => {
if (!user) {
return;
}
const { access_token } = user;
localStorage.removeItem('user')
setUser(null);
return axios.post(API.auth.logout, {
headers: {
"Authorization": `Bearer ${access_token}`,
"Content-Type": "application/json"
},
withCredentials: true
});
}
const login = async (values) => {
console.log(values);
const correctedValues = { ...values, username: values.email };
return axios.post(API.auth.login, correctedValues)
.then(res => {
const data = res.data;
processApiData(data);
})
}
const processApiData = useCallback((resp) => {
let newUser = { ...user, ...resp };
delete(newUser.user); // Delete the user sub-object since we merged that directly into the top-level object
saveUser(newUser); // Save the user
const { access_token_expiration } = newUser;
if (access_token_expiration) {
console.log("have expiration", access_token_expiration);
const nextExpiration = parseISO(access_token_expiration); // Convert from ISO 8601 to a Date Object
const earlyRefreshTime = sub(nextExpiration, { minutes: 55 }); // Do an hourish early
setAccessTokenExpiraton(earlyRefreshTime); // Set the upcoming expiraton
}
}, [user])
const refreshToken = useCallback(async () => {
const user = getUser();
const redirectToLogout = () => {
localStorage.clear(); // Clear our localStorage
setShouldGoToLogin(true);
};
if (!user) { // No user
redirectToLogout();
}
console.log(API.auth.refreshToken);
const resp = await fetch(API.auth.refreshToken, {
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({'refresh': user?.refresh_token}),
method: "POST",
withCredentials: true
})
console.log("status", resp.status);
if (resp.status === 200) {
const data = await resp.json(); // Convert to JSON
console.log("refresh token data", data);
processApiData(data);
} else {
redirectToLogout();
}
}, [processApiData])
const resetPassword = async (values) => {
return axios.post(API.auth.passwordReset, values);
}
const saveUser = async (newUser) => {
localStorage.setItem('user', JSON.stringify(newUser))
setUser(newUser)
}
const signup = async (values) => {
return axios.post(API.auth.signup, values);
}
useEffect(() => {
if (!user) {
return;
}
const interval = setInterval(()=> {
if(!user){
return false;
}
if (accessTokenExpiration) {
const now = new Date(); // Get the current time
console.log(now);
console.log(accessTokenExpiration);
if (isAfter(now, accessTokenExpiration) || isEqual(now, accessTokenExpiration)) { // If we are late to the party or the stars have aligned
refreshToken(); // Refresh the token
}
} else { // We do not have an access token expiration yet
refreshToken(); // Refresh the token immediately so we get a time
}
}, 1000 * 15)
return ()=> clearInterval(interval)
}, [accessTokenExpiration, refreshToken, user])
return (
<AuthContext.Provider value={{
getUser,
isLoggedIn,
logout,
login,
resetPassword,
signup,
user,
shouldGoToLogin
}}>
{children}
</AuthContext.Provider>
)
}
Put refreshToken function directly in your useEffect hook or wrap in useCallback.

React Native app has low performance while fetching data from firebase

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.

How to cover async function by unit test?

I'm trying to cover code below by unit test, but in code coverage report lines are still red. And I see warning that property debug is undefined. How I can successfully cover this function?
import sinon from "sinon"
import policyTesterTypeDefs from "./typeDefinitions/policyTesterTypeDefinitions"
import { typeDefs, testPolicyOnDatapoint } from "./policyTester"
describe("policyTester.js", () => {
let log
beforeEach(() => {
log = {
debug: sinon.stub(),
}
})
afterEach(() => {
log.debug.restore()
})
it("Is policy test schema valid", () => {
expect(typeDefs).to.equal(policyTesterTypeDefs)
})
it("returns valid datapoint for policy tester", async () => {
const input = {
policy: {
ingest: {
type: "ingest",
name: "zorro"
}
}
}
expect(await testPolicyOnDatapoint(input, {})).to.equal(true)
})
})
code which I'm trying to cover
import * as log from "../../logger"
import { getGrpcRequestContext } from "../../clients"
import policyTesterTypeDefs from "./typeDefinitions/policyTesterTypeDefinitions"
export const typeDefs = policyTesterTypeDefs
export async function testPolicyOnDatapoint(input, ctx) {
const { type, ...validInput } = input
log.contextLogger(ctx).debug({ validInput }, "Testing policy on datapoint")
const requestContext = getGrpcRequestContext(ctx)
try {
validInput.datapoint = inputDataToScalars(validInput.datapoint, "metadata")
if (validInput.datapoint.event !== null && validInput.datapoint.event !== undefined) {
validInput.datapoint = inputDataToScalars(validInput.datapoint, "dimensions")
}
const response = await ctx.dataSources.policyTesterSvc
.TestPolicyOnDatapoint(validInput, requestContext)
response.datapoint = response.datapoint ?
convertDatapointScalarsToArrays(response.datapoint) :
null
return response
} catch (e) {
log.contextLogger(ctx).warn(e, "Failed to test policy on datapoint")
throw (e)
}
}
code coverage report

Error: Invalid hook call. Hooks can only be called inside of the body of a function component in React Native

I am trying to use useSelector within the component still I am getting error saying: Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
backgroundTasks.js:
import axios from "axios";
import AsyncStorage from '#react-native-community/async-storage';
import { baseURL, mlURL } from "../../constants";
import BackgroundFetch from "react-native-background-fetch";
import { useSelector } from "react-redux";
import { showNotification, handleScheduleNotification, handleCancel } from "./notification.android";
const getLatestNotifications = async (headers, user_id) => {
const Link = `${baseURL}/api/push-notifications`;
console.log("Push notification Link is", Link);
try {
let data = await axios
.get(
Link,
{ headers: headers }
);
if (data.data.response) {
console.log("Recieved notification response", data.data.response);
return data.data.response;
}
else {
return [];
}
} catch (err) {
console.log("Notifications error", err);
return [];
}
}
//In startTask I want to use useSeletor but I am getting error.
const startTask = async (task = "notifications") => {
console.log("Background task started");
console.log('background');
const token = await AsyncStorage.getItem("token");
const user_id = await AsyncStorage.getItem("user_id");
const userName = await AsyncStorage.getItem("name");
const notificationsUnReadNumber = useSelector((state) => state.notification.notificationCount); //Here
console.log(notificationsUnReadNumber);
const apiHeaders = {
'x-access-token': token,
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:84.0) Gecko/20100101 Firefox/84.0',
'Accept': 'application/json, text/plain, */*',
};
if (task == "notifications" && token) {
let notifications = await getLatestNotifications(apiHeaders, user_id);
console.log("Get Latest Notifications data", notifications);
if (notifications && notifications.length > 0 && notificationsUnReadNumber !==0) {
console.log('inside notification');
notifications.forEach((notification) => {
showNotification(notification.title, notification.content, String(notification._id));
});
}
}
};
const inititalizeBackgroundTasks = async () => {
const onEvent = async (taskId) => { //This task will run when app is not terminated (foreground/background)
console.log('[BackgroundFetch] task: ', taskId);
// Do your background work...
console.log("Task background called")
console.log("Received background-fetch event: ", taskId);
startTask("notifications");
BackgroundFetch.finish(taskId);
}
// Timeout callback is executed when your Task has exceeded its allowed running-time.
// You must stop what you're doing immediately BackgorundFetch.finish(taskId)
const onTimeout = async (taskId) => {
console.warn('[BackgroundFetch] TIMEOUT task: ', taskId);
BackgroundFetch.finish(taskId);
}
let status = await BackgroundFetch.configure({
minimumFetchInterval: 15, //Run Task every 15 minutes
// Android options
forceAlarmManager: true, // <-- Set true to bypass JobScheduler.
stopOnTerminate: false,
startOnBoot: true,
enableHeadless: true,
requiredNetworkType: BackgroundFetch.NETWORK_TYPE_NONE, // Default
requiresCharging: false, // Default
requiresDeviceIdle: false, // Default
requiresBatteryNotLow: false, // Default
requiresStorageNotLow: false // Default
}, onEvent, onTimeout);
console.log('[BackgroundFetch] configure status: ', status);
};
export { inititalizeBackgroundTasks};
If I have to make this a component then How can I export backgroundTasks as default and export {inititalizeBackgroundTasks} as normal?
I want to export only one component which is inititalizeBackgroundTasks and use others as a function inside my component so how can I use useSelector if I am doing it in the wrong way?
Only Call Hooks from React Functions
✅ Call Hooks from React function components.
✅ Call Hooks from custom Hooks
Learn more about Rules of Hooks in the documentation.
I hope this work around helps you (as you mentioned above) we create backgroundTasks export default as a functional component so we can use react hooks.
import { useEffect } from "react";
import axios from "axios";
import AsyncStorage from "#react-native-community/async-storage";
import { baseURL, mlURL } from "../../constants";
import BackgroundFetch from "react-native-background-fetch";
import { useSelector } from "react-redux";
import {
showNotification,
handleScheduleNotification,
handleCancel,
} from "./notification.android";
const getLatestNotifications = async (headers, user_id) => {
const Link = `${baseURL}/api/push-notifications`;
console.log("Push notification Link is", Link);
try {
let data = await axios.get(Link, { headers: headers });
if (data.data.response) {
console.log("Recieved notification response", data.data.response);
return data.data.response;
} else {
return [];
}
} catch (err) {
console.log("Notifications error", err);
return [];
}
};
//In startTask I want to use useSeletor but I am getting error.
const startTask = async (task = "notifications", notificationsUnReadNumber) => {
console.log("Background task started");
console.log("background");
const token = await AsyncStorage.getItem("token");
const user_id = await AsyncStorage.getItem("user_id");
const userName = await AsyncStorage.getItem("name");
console.log(notificationsUnReadNumber);
const apiHeaders = {
"x-access-token": token,
"User-Agent":
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:84.0) Gecko/20100101 Firefox/84.0",
Accept: "application/json, text/plain, */*",
};
if (task == "notifications" && token) {
let notifications = await getLatestNotifications(apiHeaders, user_id);
console.log("Get Latest Notifications data", notifications);
if (
notifications &&
notifications.length > 0 &&
notificationsUnReadNumber !== 0
) {
console.log("inside notification");
notifications.forEach((notification) => {
showNotification(
notification.title,
notification.content,
String(notification._id)
);
});
}
}
};
export const inititalizeBackgroundTasks = async (notificationsUnReadNumber) => {
let status = await BackgroundFetch.configure(
{
minimumFetchInterval: 15, //Run Task every 15 minutes
// Android options
forceAlarmManager: true, // <-- Set true to bypass JobScheduler.
stopOnTerminate: false,
startOnBoot: true,
enableHeadless: true,
requiredNetworkType: BackgroundFetch.NETWORK_TYPE_NONE, // Default
requiresCharging: false, // Default
requiresDeviceIdle: false, // Default
requiresBatteryNotLow: false, // Default
requiresStorageNotLow: false, // Default
},
async (taskId) => {
//This task will run when app is not terminated (foreground/background)
console.log("[BackgroundFetch] task: ", taskId);
// Do your background work...
console.log("Task background called");
console.log("Received background-fetch event: ", taskId);
startTask("notifications", notificationsUnReadNumber);
BackgroundFetch.finish(taskId);
},
// Timeout callback is executed when your Task has exceeded its allowed running-time.
// You must stop what you're doing immediately BackgorundFetch.finish(taskId)
async (taskId) => {
console.warn("[BackgroundFetch] TIMEOUT task: ", taskId);
BackgroundFetch.finish(taskId);
}
);
console.log("[BackgroundFetch] configure status: ", status);
};
const backgroundTasks = () => {
const notificationsUnReadNumber = useSelector(
(state) => state.notification.notificationCount
); //Here
useEffect(() => {
if (notificationsUnReadNumber) {
inititalizeBackgroundTasks(notificationsUnReadNumber);
}
}, [notificationsUnReadNumber]);
};
export default backgroundTasks;
import the useSelector in the component where you call startTask() and add it as the second parameter like startTask('notifications', useSelector). This should solve your problem.

Categories

Resources