javascriptreactjsfirebasegoogle-cloud-firestoreasynchronous-javascript

How to get asynchronous data into a global array in React.js?


I want to insert asynchronous data from Firestore into React elements. I have a couple of functions that handle the flow.

getEvents() is an asynchronous function that returns an array of objects that is my data from Firestore.

const getEvents = async() => {
    try {
        const data = query(collection(db, id));
        const events = [];
        const snapshot = await getDocs(data);
        snapshot.forEach((doc) => events.push(doc.data()));
        return events;
    } catch(error) {
        console.error(error);
    }
}

This function is referenced in receiveEvents() where I take the returned data and put it into a global array in order to use it in the DOM.

let userEvents = [];
const receiveEvents = () => {
    getEvents()
        .then(result => userEvents = result)
        .catch(error => console.error(error));

This function is used in displayEvents() to paste the returned data into the desired element. The function is called upon a button click.

const displayEvents = () => {
    try {
        const btnContainer = document.querySelector(".btn-container");
        ReactDOM.render(<AppNavigation />, btnContainer);
        receiveEvents().then(() => { return userEvents }); 
    } catch(error) {
        console.error(error);
    }
}

I get an error index.js:1 TypeError: Cannot read properties of undefined (reading 'then') @ displayEvents.

These functions use the logged user ID to access the right directory in the database. I retrieve the ID in a given function declared at the top of the file.

let id = null;
const getUserId = () => {
    try {
        console.log("getUserId()");

        return auth.currentUser.uid;
    } catch(error) {
        console.error(error);
    }
}

The <AppNavigation/> component returns a div, namely:

<div className="arrow-navigation-container">
      <button className="arrow-btn"><span className="icon arrow">arrow_back</span></button> 
      <button className="arrow-btn"><span className="icon arrow">arrow_forward</span></button>
</div>

What can I do to get the asynchronous data into the userEvents array, so I can show it to the user upon request?


Solution

  • I would use hooks for this personally:

    Examples how to use those hooks: https://reactjs.org/docs/hooks-reference.html#usestate

    The useState would go into the same component as the useContext Provider, usually in example apps it's in component. The component which needs the data needs to be a child of the Context.Provider, but it doesn't need to be a direct child.

    useEffect goes also to the same component, and with the dependencies you can decide if you want to fetch the data only once or for example when that ID changes.