React: Clean-Up function never called - javascript

I'm building a react app that uses Firebase and Firestore.
I'm using the onSnapshot function so I get the real time data from Firestore, but I was wondering how I can remove that listener.
Yeah I know, I must use the cleanup function of the useEffect hook, but I can't make it work, here's my code:
useEffect(() => {
let removeListener = getCanzoni(utente).onSnapshot(function (querySnapshot) {
let promises = querySnapshot.docs.map(async function (doc) {
let canzone = doc.data();
canzone.id = doc.id;
return canzone;
});
Promise.all(promises).then((canzoni) => {
cambiaCanzoni(canzoni);
});
return function cleanup() {
console.log("Removed Listener");
removeListener();
};
});
}, []);
The getCanzoni function is imported from another files and it's definition is:
export function getCanzoni(utente) {
return firestore
.collection("Canzoni")
.where("utente", "==", utente.uid)
.orderBy("data", "desc");
}
When I remove the component, I don't see the 'Removed Listener' in the console.
I know that the clean-up function is called when the dependency array changes or when the components is unmounted.
Any idea or tips?

I've found the error:
The clean up function must be defined in the hook's function body, not inside other function, like this:
useEffect(() => {
let removeListener = getCanzoni(utente).onSnapshot(function (querySnapshot) {
let promises = querySnapshot.docs.map(async function (doc) {
let canzone = doc.data();
canzone.id = doc.id;
return canzone;
});
Promise.all(promises).then((canzoni) => {
cambiaCanzoni(canzoni);
});
});
return function cleanup() {
console.log("Tolto il listener");
removeListener();
};
}, []);

Related

useEffect hook to update state from a function that returns a promise

I have a function that returns an object.
It looks like this
const aggResults = data => {
const { surveyName, questionId } = data
const db = firebase.firestore()
const surveyData = db.collection('surveys').doc(surveyName)
return surveyData.onSnapshot(doc => {
console.log('test', doc.data().aggResults[questionId])
return doc.data().aggResults[questionId]
})
}
Frome the same file, I call my function like this
{
...
output: {
...
function: () => aggResults({ surveyName: '...', questionId: '...' }),
},
},
In a separate file, I want to update my state based with what aggResults returns.
I have used a useEffect hook to do this
const [data, updateData] = useState({})
useEffect(() => {
if (!_.isUndefined(question.output)) {
question.output.function().then(res => updateData(res))
} else {
console.log('q', question.output)
}
}, [question])
The error I'm getting at the moment is TypeError: question.output.function().then is not a function
Where am I going wrong?
onSnapshot doesn't return a promise. It returns an unsubscribe function that you call to remove the listener from the DocumentReference. You can see an example of this in the documentation. When you call question.output.function(), it is going to return you this unsubscribe function.
Snapshot listeners are for receive updates to a document as it changes over time. If you're trying to do a single query without listening for ongoing changes, you should use get() instead of onSnapshot(), as shown in the documentation. get() returns a promise that resolves with a DocumentSnapshot contains the document data.

How to reset recaptcha on Success for signInWithPhoneNumber()

I am using Firebase.auth()signInWithPhoneNumber(number, appVerifier)
Everything is working as expected however I am trying to resolve the issue below:
Here is my implementation:
useEffect(() => {
window.recaptchaVerifier = new app.auth.RecaptchaVerifier("sendBtn", {
size: "invisible",
callback: function () {
onSend();
},
});
});
const onSend = (value) => {
const appVerifier = window.recaptchaVerifier;
const setMobile = "valid mobile..";
firebase
.auth()
.signInWithPhoneNumber(setMobile, appVerifier)
.then(function (confirmationResult) {
appVerifier.reset()
console.log(confirmationResult)
})
.catch(function (error) {
appVerifier.reset()
console.log(error);
});
};
How can I correctly handle Recaptcha? Without it being rendered multiple times. I'm looking to destroy it on Recaptcha on success, I have gone through the documentation here but clear() or reset() does not seem to work
You can provide a empty array of dependencies to useEffect to trigger only after initial render, more details in this Stack Overflow Answer.
Additionally it may be a good idea to add an if check to see if window.recaptchaVerifier is set (in case you have component using recaptcha anywhere else on your page), before trying to initialize a new RecaptchaVerifier.
useEffect(() => {
if (!window.recaptchaVerifier) {
window.recaptchaVerifier = new app.auth.RecaptchaVerifier('sendBtn', {
size: 'invisible',
callback: function () {
onSend();
}
});
}
}, []);
You have to provide the dependencies of your useEffect otherwise it will be executed each time the component render.
useEffect(() => {
// recaptcha
}, [])

How to use jest to test a function's return value(array)?

Here is my function to recursively loop through a object:
const helper = (obj,list) =>{
for (var property in obj){
if(obj.hasOwnProperty(property)) {
if(typeof obj[property] == "object") {
helper(obj[property],list);
}else {
if (property === "$ref"){
if(!list.includes(obj[property])){
list.push(obj[property]);
}
}
}
}
}
return list;
};
The object is simple, please see below:
{
"person": {
"properties": {
"sex":{
"type":"string"
},
"name": {
"$ref": "#/person/properties/sex"
}
}
}
}
The the helper will finally return a list ['#/person/properties/sex']
To run the code, just do helper(some_obj,[])
Here is my jest test code:
describe('helper function test', () =>{
it('should return a list', () =>{
const mock = jest.fn((obj,[]) => helper(obj,[]));
const list = mock(obj,[]);
expect(list).toMatch(['something']);
});
});
I have also tried:
describe('helper function test', () =>{
it('should return a list', () =>{
const list = helper(obj, []);
expect(list).toMatch(['something']);
});
});
The jest tells me the expect object is a array but it's value is [], which means empty. Actually I did a manually test for helper function in the function file, the return has no problem, which is what I expect.
One thing to mention, I use this helper inside of a promise later, I do not know the issue is related to promise, since the promise function has not called. I even tried to comment out the promise function, still no luck.
Would someone tells me how to get the real result from the jest? I would really appreciate any helps here! Thank you for your time.
I don't think you need to use jest.fn in here. If you want to test your function, you can do this:
describe('helper function test', () =>{
it('should return a list', () =>{
const list = helper(obj, []);
expect(list).toEqual(['something']);
});
});

Using state variable in function, cannot read property x of undefined (Functional React)

I am using firebase with react functional components, I am defining db object in useEffect and setting the object to state like this :
const [Db, setDb] = useState();
useEffect(() => {
const db = firebase.database();
setDb(db, checkStatus());
}, []);
const checkStatus = () => {
Db.ref("orders")
.once("value")
.then(snapshot => {
// do something
});
}
I have given checkStatus() as a callback to setDb() which I belive will get executed after setDb() changes the state (normally calling checkStatus() in useEffect() also gives the same error). Now while calling checkStatus(), I get the following error :
TypeError: Cannot read property 'ref' of undefined
I believe checkStatus() is getting executed before state changed, which leads to Db being undefined. I cannot figure out a way to call a function after the state value is set to something, and what are the best practices while calling functions and using state as such.
You can use useEffect
const [Db, setDb] = useState();
useEffect(() => {
const db = firebase.database();
setDb(db);
}, []);
useEffect(() => {
if(Db) {
Db.ref("orders")
.once("value")
.then(snapshot => {
// do something
});
}
}, [Db]);
Or you can set firebase in useState
const [Db, setDb] = useState(() => firebase.database());
useEffect(() => {
Db.ref("orders")
.once("value")
.then(snapshot => {
// do something
});
}, []);
Extract a custom hook, useDBStatus().
This custom hook abstracts any DB-related communication and just return what's relevant (i.e status).
function useDBStatus(ref) {
const db = firebase.database();
const [status, setStatus] = React.useState({});
React.useEffect(() => {
if (db) {
db(ref)
.once("value")
.then(snapshot => {
setStatus(/* something */);
});
}
return () => { /* close db connection */ };
}, [db, ref]);
return status;
}
function Component() {
const status = useDBStatus("orders");
// use status
}

Angular Observables return anonymous function

I have this method that I copied from a websocket tutorial but I don't understand the meaning of the "return () => { ... }" inside the observable ? Can someone explain me what is the purpose of that ?
public onMessage(topic: string, handler = SocketClientService.jsonHandler) : Observable<any> {
return this.connect().pipe(first(), switchMap(client => {
return new Observable<any>(observer => {
const subscription : StompSubscription = client.subscribe(topic, message => {
observer.next(handler(message));
});
return () => {
console.log("Unsubscribe from socket-client service");
client.unsubscribe(subscription .id);
}
});
}));
}
In order to create an Observable, you can use new Observable or a creation operator. See the following example:
const observable = new Observable(function subscribe(subscriber) {
subscriber.next(1);
subscriber.next(2);
subscriber.next(3);
});
You can provide a function unsubscribe() to allow dispose of resources, and that function goes inside subscribe() as follows:
const observable = new Observable(function subscribe(subscriber) {
subscriber.next(1);
subscriber.next(2);
subscriber.next(3);
return function unsubscribe() {
console.log('Clearing resources on observable');
};
});
Of course, you can use an arrow function expression to have:
const observable = new Observable((observer) => {
observer.next(1);
observer.next(2);
observer.next(3);
return () => {
console.log('Clearing resources on observable');
};
});
Try the following code to test the Observable:
const subscription = observable.subscribe(res => console.log('observable data:', res));
subscription.unsubscribe();
Finally, subscription.unsubscribe() is going to remove the socket connection in your example.
Find a project running with these examples here: https://stackblitz.com/edit/typescript-observable-unsubscribe
Let me know if that helps!

Categories

Resources