user7455332
user7455332

Reputation:

Reference error, says variable is not defined

So I have a function which reads from Firebase database and then creates an array of objects. I have defined variable key, but it is still unavailable.

var usersList = [];

const ref = firebase.database().ref()

function fetchUsers() {

    ref.child('users').once('value').then(snap => {
        var promises = [];

        snap.forEach(childSnap => {

            var key = childSnap.key

            promises.push
                (ref.child(`users/${key}/points`).once('value')
            );


        });

        return Promise.all(promises);

    }).then(function(snapshots) {
        return snapshots.map(snapper => {
        var points = snapper.val()
        return {uid: key, points: points};
        })
    }).then(function(usersList) {
        console.log(usersList)
    })

}

And this is the error I get...

(node:11724) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): ReferenceError: key is not defined

If i just do: key = childSnap.key, then every object's uid is the same.

Upvotes: 1

Views: 1591

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1074138

key is only defined within the forEach callback. Naturally you cannot then reference it from within the second then callback. Moreover, which key would it be? For the first entry? The second? Third?

Instead, you need to return the key with the value:

function fetchUsers() {
    ref.child('users').once('value').then(snap => {
        var promises = [];
        snap.forEach(childSnap => {
            var key = childSnap.key;
            promises.push(
                ref.child(`users/${key}/points`).once('value').then(
                    snapper => ({key, snapper})    // **
                )
            );
        });

        return Promise.all(promises);
    }).then(function(snapshots) {
        return snapshots.map(({key, snapper}) => { // **
            var points = snapper.val();
            return {uid: key, points: points};
        });
    }).then(function(usersList) {
        console.log(usersList);
    });
}

On the first ** line above, we're transforming the result from once so it returns an object with both the key and "snapper".

On the second ** line, we're using destructured parameters to receive those objects as the distinct key and snapper parameters.


FWIW, if you want to aggressively use concise arrows:

function fetchUsers() {
    ref.child('users').once('value').then(snap =>
        Promise.all(snap.map(childSnap => {
            const key = childSnap.key;
            return ref.child(`users/${key}/points`)
                        .once('value')
                        .then(snapper => ({key, snapper}));
        }))
    ).then(snapshots =>
        snapshots.map(({key, snapper}) => ({uid: key, points: snapper.val()}))
    ).then(usersList => {
        console.log(usersList);
    });
}

Also note the use of map rather than declaring an array and pushing to it from a forEach callback. :-)

Upvotes: 1

Related Questions