How to extract Firebase data to variable after setState() with react?

Viewed 192

First time using react and firebase real-time database. I'm having a hard time extracting the data and inserting it into const items. From my understanding, the firebase code is asynchronous, so the code will execute after the page is loaded up.

After checking the console numerous times, I see the data loads into const tasks array. However, the data inside tasks[] doesn't display when I put it inside the items array(title, subtitle, etc). I've looked at multiple solutions on this and none of them have answered my issue.

I thought it was because of the items being a const, but after changing it to var, the issue persisted. Any sort of guidance would be appreciated here.

One small note: using await doesn't seem to work for me whatsoever. I could be mixing things up due to my lack of knowledge of firebase + react.

const Timelines = () => {

    const [tasks, setTasks] = useState([]);

    useEffect(() => {
        const ref = firebase.database().ref();
        const listener = ref.on('value', snapshot => {
            const fetchedTasks = [];
            snapshot.forEach(childSnapshot => {
                const key = childSnapshot.key; //using await here doesn't work. Return 'await is a reserved word error'
                const data = childSnapshot.val(); //using await here doesn't work. Return 'await is a reserved word error'
                fetchedTasks.push({ id: key, ...data });
            });
            setTasks(fetchedTasks);
        });
        return () => ref.off('value', listener);
    }, [firebase.database()]);



    const items = [{
        title: "Header" + tasks,
        cardTitle: "Title",
        //url: "http://www.history.com",
        cardSubtitle: "LA",
        cardDetailedText: ["Paragrah"],
        media: {
            type: "IMAGE",
            source: {
                url: "./images/image1.jpeg"
            }
        }
    },
        {
            title: "Title",
            cardTitle: "Title1",
            //url: "http://www.history.com",
            cardSubtitle: "LA",
            cardDetailedText: "Paragraph 1",
            media: {
                type: "IMAGE",
                source: {
                    url: "./images/img-2.jpg"
                }
            }
        },
    
    
    ]
    return (
        <div className='timeline' style={{ width: "auto", height: "auto" }}>
            <Chrono items={items}
                slideShow
                mode="VERTICAL_ALTERNATING"
                theme={{
                    primary: "#7e8083",
                    secondary: "white",
                    cardBgColor: "#00467f",
                    cardForeColor: "white",
                    titleColor: "#00467f"
                }}
                useReadMore="true" />
        </div>
        
    ) 
  };

export default Timelines;
2 Answers

Your problem may be rerendering problem. So you can try loading state. Check my useEffect and return.

const Timelines = () => {

    const [tasks, setTasks] = useState([]);
    const [loading, setLoading] = useState(false); //<----


    useEffect(() => {
        setLoading(true) //<-----
        const ref = firebase.database().ref();
        const listener = ref.on('value', snapshot => {
            const fetchedTasks = [];
            snapshot.forEach(childSnapshot => {
                const key = childSnapshot.key; //using await here doesn't work. Return 'await is a reserved word error'
                const data = childSnapshot.val(); //using await here doesn't work. Return 'await is a reserved word error'
                fetchedTasks.push({ id: key, ...data });
            });
            setTasks(fetchedTasks);
            setLoading(false) //<---
        });
        return () => ref.off('value', listener);
    }, [firebase.database()]);



    const items = [{
        title: "Header" + tasks,
        cardTitle: "Title",
        //url: "http://www.history.com",
        cardSubtitle: "LA",
        cardDetailedText: ["Paragrah"],
        media: {
            type: "IMAGE",
            source: {
                url: "./images/image1.jpeg"
            }
        }
    },
        {
            title: "Title",
            cardTitle: "Title1",
            //url: "http://www.history.com",
            cardSubtitle: "LA",
            cardDetailedText: "Paragraph 1",
            media: {
                type: "IMAGE",
                source: {
                    url: "./images/img-2.jpg"
                }
            }
        },
    
    
    ]
    return !loading &&  (
        <div className='timeline' style={{ width: "auto", height: "auto" }}>
            <Chrono items={items}
                slideShow
                mode="VERTICAL_ALTERNATING"
                theme={{
                    primary: "#7e8083",
                    secondary: "white",
                    cardBgColor: "#00467f",
                    cardForeColor: "white",
                    titleColor: "#00467f"
                }}
                useReadMore="true" />
        </div>
        
    ) 
  };

export default Timelines;

Thank you

What Gökhan Geyik stated as a solution is correct. But I had to adjust my code a little since my Firebase db structure was different. The problem was, rerendering!

 useEffect(() => {
        setLoading(true)
        const ref = firebase.database().ref('Overview/events');
        const listener = ref.on('value', snapshot => {
            const fetchedTasks = [];
            const data = snapshot.val();
            fetchedTasks.push(data);
            setTasks(fetchedTasks);
            setLoading(false)
        });
        return () => ref.off('value', listener);
    }, [firebase.database()]);

return !loading && ( 
   ....
)

This worked like a charm. Much kudos to Gökhan Geyik for the right direction!

Related