Reputation: 2460
See jsbin.com/ceyiqi/edit?html,console,output for a verifiable example.
I have a reference listening to a database point
jobs/<key>
/list
where <key>
is the teams unique number
Under this entry point is a list of jobs
I have a listener on this point with
this.jobsRef.orderByChild('archived')
.equalTo(false)
.on('child_added', function(data) {
I also have a method that does the following transaction:
ref.transaction(function(post) {
// correct the counter
if(post)
{
// console.log(post);
if(active)
{
// if toggeling on
}
else
{
// if toggeling off
}
}
return post;
})
When invoking the transaction the child_added
is also invoked again, giving me duplicate jobs.
Is this expected behavior? Should I simply check to see if the item has been got before and add to the array accordingly? Or am I doing something wrong?
Thanks in advance for your time
Upvotes: 1
Views: 275
Reputation: 598728
You're hitting an interesting edge case in how the Firebase client handles transactions. Your transaction function is running twice, first on null
and then on the actual data. It's apparent to see if you listen for value
events on /jobs, since then you'll see:
null
) The null
value in step 2 is the client's initial guess for the current value. Since the client doesn't have cached data for /jobs (it ignores the cached data for /jobs/list), it guesses null. It is clearly wrong. But unfortunately has been like this for a long time, so it's unlikely to change in the current major version of the SDK.
And because of the null
in step 2, you'll get child_removed
events (which you're not handling right now) and then in step 3 you'll get child_added
events to re-add them.
If you handled the child_removed
events, you're items wouldn't end up duplicated, but they would still disappear / reappear, which probably isn't desirable. Another workaround in the current setup is to explicitly tell the transaction to not run with the local estimate, which you can do by passing in false
as the third parameter:
function transact() {
var path = 'jobs/';
var ref = firebase.database().ref(path);
ref.transaction(function(post) {
return post;
}, function(error, committed, snapshot) {
if (error) {
console.log('Transaction failed abnormally!', error);
} else if (!committed) {
console.log('Transaction aborted.');
} else {
console.log('Transaction completed.');
}
}, false);
}
I'm sorry I don't have a better solution. But as I said: it's very unlikely that we'll be able to change this behavior in the current generation of SDKs.
Upvotes: 1