telpel225
telpel225

Reputation: 15

Undefined Array(but its not) when retrieved from firebase realtime database

I am trying to retrieve the data from firebase realtime database and then pass it another function. But it throws an error saying TypeError: undefined is not an object (evaluating 'notesList.map')

function NotesList() {

    const[notesList, SetNotesList] = useState();

    useEffect(() => {
        const NoteRef = firebase.database().ref('localnotes-data');
        NoteRef.on('value', (snapshot)=> {
            const notes = snapshot.val();
            const container = [];
            for(let id in notes){
                container.push({id, ...notes[id] });
            }
            if (container){
                SetNotesList(container);
            }
        }, []);
    })
    console.log(notesList);

    return (
        <View style={styles.list}>
            <ScrollView>
                <Search />
                <Add />
                {notesList.map((note) => (<Note text = {text} date = {date}/>))}
            </ScrollView>
        </View>
    )
}

const styles = StyleSheet.create({
    list: {
        marginTop: 0,
        marginBottom: 145,
    }
})
export default NotesList

This is console.log(notesList)

enter image description here

Upvotes: 0

Views: 244

Answers (3)

GregMit
GregMit

Reputation: 507

Because noteList is undefined -> const[notesList, SetNotesList] = useState();

So check that noteList exists to call map when you asynchronous fetch is finished.

You can also initialise you noteList like so -> const[notesList, SetNotesList] = useState([]);

The following code should work tho.

...
    return (
        <View style={styles.list}>
            <ScrollView>
                <Search />
                <Add />
                {notesList && notesList.map((note) => (<Note text = {text} date = {date}/>))}
            </ScrollView>
        </View>
    )
}
...

Upvotes: 1

samthecodingman
samthecodingman

Reputation: 26306

As an alternative to @Dharmaraj's answer, you can also introduce a "loading" variable like so:

Note: Make sure to take a look at the other changes like some variable names, using DataSnapshot#forEach() to maintain the order from the query, detaching the snapshot listeners, snapshot error-handling and making sure the key property is set in the map() function.

let renderCount = 0; // just for debugging, remove it later

function NotesList() {

    const [notesList, setNotesList] = useState();
    const notesLoading = notesList === undefined;

    useEffect(() => {
        const notesQueryRef = firebase.database()
            .ref('localnotes-data');
        // you can add `orderByChild()`, etc. to the above query

        const listener = notesQueryRef.on(
            'value',
            (snapshot) => {
                const notesArray = [];
                snapshot.forEach(noteSnapshot =>
                    const id = noteSnapshot.key;
                    notesArray.push({
                        id: noteSnapshot.key,
                        ...noteSnapshot.val()
                    });
                );

                setNotesList(notesArray);
            },
            (error) => {
                // TODO: Handle errors better than this
                console.error("Failed to get notes: ", error);
            }
        );

        // return cleanup function
        return () => {
            notesQueryRef.off('value', listener);
        };
    }, []);

    // just for debugging, remove it later
    console.log({
        renderCount: ++renderCount,
        notesLoading,
        notesList
    });

    return (
        <View style={styles.list}>
            <ScrollView>
                <Search />
                <Add />
                {notesLoading
                    ? <Spin tip="Loading..." key="loading" />
                    : notesList.map(note => (<Note
                          text={note.text}
                          date={note.date}
                          key={note.key}
                      />));
                }
            </ScrollView>
        </View>
    )
}

const styles = StyleSheet.create({
    list: {
        marginTop: 0,
        marginBottom: 145,
    }
})
export default NotesList

Upvotes: 1

Dharmaraj
Dharmaraj

Reputation: 50930

I think that's because initial state of notesList is undefined. Try setting that to an empty array.

const [notesList, SetNotesList] = useState([]);
                                           ^^

Now notesList is defined and you can run the map() method on it.

Upvotes: 0

Related Questions