Gabi C
Gabi C

Reputation: 481

Fetching data from Firestore

So I am trying to fetch data from Firestore, when I console log it I got the content of my collection back, but when I move the code to a function I am not able to return it back.

This code works:

const db = firebase.firestore();
db.settings({ timestampsInSnapshots: true});
db.collection('story').get().then((snapshot) => {
snapshot.docs.forEach(doc => {console.log(doc.data())
    ;})
})

This doesnt work. (it compiles, but doesn`t return anything):

...
getMyStory = () => {
        const db = firebase.firestore();
        db.settings({ timestampsInSnapshots: true});
        db.collection('story').get().then((snapshot) => {
        snapshot.docs.forEach(doc => {
            let items = doc.data();
        })
        });
        return this.items;
    }


    render () {


        return (
        <p>{this.getMyStory}</p>
);
}

What am I doing wrong?

Upvotes: 3

Views: 16793

Answers (5)

klimqx
klimqx

Reputation: 147

if any one is using React with Redux with Firebase and has difficulty here is how to do it here i get all the publications from firestore using redux

// publicationsActions.js

var publicationsRef = db.collection("publications")
var publicationsList = []

// Function that returns the publications list
export const getPublications = () => async (dispatch) => {
    await publicationsRef.get().then((res) => {
        res.docs.forEach(doc => {
            publicationsList.push(doc.data())
        })
        dispatch({
            type: GET_PUBS,
            payload: publicationsList
        })
    })
}


// publicationReducer.js

export default function (state = initialState, action) {
    switch(action.type){
        case GET_PUBS:
            return {
                ...state,
                items: action.payload
            }
        case ADD_PUB:
            return {
                ...state,
                items: [action.payload, ...state.items]
            }
        default:
            return state; 
    }
}

Upvotes: 0

kai_onthereal
kai_onthereal

Reputation: 488

import React, { Component } from "react";
import firebase from "../config";

class App extends Component {
  constructor(props) {
    super(props);

    // Reference firebase in this.state, creating a global key-value pair
    this.state = {
      roomsRef: firebase.firestore().collection("rooms"),
      rooms: []
    };
  }

  // Retrieve data from firebase and set state
  getDb(db) {
    db.get().then(querySnapshot => {
      querySnapshot.forEach(doc => {
        let room = doc.data();
        room.key = doc.id;
        this.setState({
          rooms: this.state.rooms.concat(room)
        });
      });
    });
  }

  // 
  componentDidMount() {
    this.getDb(this.state.roomsRef);
  }

  render() {
    const rooms = this.state.rooms.map((r, i) => <li key={i}>{r.name}</li>);

    return (
      <div className="App">
        <section className="room-list">
          {this.state.rooms.length === 0 ? (
            <p>loading...</p>
          ) : (
            <ul>{rooms}</ul>
          )}
        </section>
      </div>
    );
  }
}

export default App;

Upvotes: 0

ene_salinas
ene_salinas

Reputation: 705

The Promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value

Based in your code return this.items; executed first and then resolve db.collection('story').get(), finally never get the results.

Basically this line:

db.collection('story').get()

it's a promise then you must wait that resolve the result, below code:

getMyStory = () => {
    const db = firebase.firestore();

    db.settings({ timestampsInSnapshots: true});

    return db.collection('story').get()
}

Read More About Promises

=======================EDIT=========================

getMyStory().then((snapshot) => {
    const listItems = snapshot.map((element) =>
      <li>{element}</li>
    );

    ReactDOM.render(
      <ul>{listItems}</ul>,
      document.getElementById('root')
    );
});

Read More About Map

Upvotes: 1

Tsuni
Tsuni

Reputation: 5818

The main problem here is that you are trying to render async data synchronously, which isn't possible to do with react (not yet at least).

When rendering async data you will usually leverage component state.

Below is a standard usage pattern when loading and rendering something async.

class YourComponent extends Component {
  state = {
    items: []
  }

  // This method is called when the component mounts
  // Usually  a good place to do async requests
  componentDidMount() {

    db.collection('story').get().then(snapshot => {
      // After promise is finished set data to local state
      // When setting state the render method will be called, thus rerendering the UI
      this.setState({ items: snapshot })
    })
  }

  render() {
    // Access your items from local component state
    const { items } = this.state;

    return (
      <div>
        {items.forEach(doc => {
          // Render whatever data is in your document
          return <p key={doc.id}> { Your data goes here }</p>
        })
        }
      </div>
    )
  }
}

Upvotes: 1

Dacre Denny
Dacre Denny

Reputation: 30390

Your rendering logic will need to account for the query to Firebase being asynchronous. Consider making use of your components state to resolve this, by making the following adjustments to your code:

getMyStory() { /* Remove arrow function */

    const db = firebase.firestore();
    db.settings({ timestampsInSnapshots: true});
    db.collection('story').get().then((snapshot) => {

      snapshot.docs.forEach(doc => {
          let items = doc.data();

          /* Make data suitable for rendering */
          items = JSON.stringify(items);

          /* Update the components state with query result */
          this.setState({ items : items }) 
      });

    });
}

Next, add componentDidMount() to your component, and then add the call to getMyStory() like so:

componentDidMount() {

    /* Cause your component to request data from Firebase when
       component first mounted */
    this.getMyStory()
}

Finall, update your render method to use the state, rather than method:

  render () {

    return (<p>{ this.state.items || 'Loading' }</p>);
 }

Hope this helps!

Upvotes: 10

Related Questions