1

i am building a simple React app where i collect data from a Firebase realtime database and push it as an array (memberArr) into the component state via setState:

this.setState({members: memberArr})

When I log the state in the render() method, I see the contents of the array. In the React Dev Tools the state is also filled as expected.

If I now want to access the contents of the array (e.g. with this.state.members[0]) the console returns undefined.

I initialize the state like this:

constructor(props) {
  super(props);
  this.state = {
      members: [],
  };
}

My whole componentDidMount() method looks like this:

componentDidMount() {

    const membersRef = firebase.database().ref(`${groupName}/members`);

    membersRef.on('value', (data) => {

      const memberArr = []; 

      data.forEach(function(snapshot){

        //Gets name of member
        var memberName = snapshot.val().name;

        //Gets UID of member
        var memberKey = snapshot.key;

        //Get expenses from fetched member. When ready call function to push everything as array into component state.
        this.getExpensesFromUser(memberKey).then(function(data) {
          pushArray(data)
        });

        function pushArray(memberExpense) {
          memberArr.push([memberName, memberKey, memberExpense])
        };

        //This works:
        //memberArr.push([memberName, memberKey])

      }.bind(this));

      this.setState({members: memberArr})

    });
  }

On a side note: If I avoid calling

this.getExpensesFromUser(memberKey).then(function(data) {
          pushArray(data)
        });

and only use the following to push the name and key of the members:

memberArr.push([memberName, memberKey])

everything works as expected. console.log(this.state.members[0][0]) returns the name of the first member after the render() method gets triggered due to setState.

Do you have any tips for me?

5
  • Check the array is filled before map through it at render. Commented Apr 4, 2020 at 11:39
  • 1
    this.getExpensesFromUser(memberKey) is async and you're setting the state before actually receiving the values, so your state is always [] Commented Apr 4, 2020 at 11:41
  • @DragoşPaulMarinescu But why am I seeing the correct content oft the state in the React Dev Tools? Moreover: If I console.log the state inside the render() method it also shows me the content I expect. Commented Apr 4, 2020 at 11:47
  • 1
    instead of pushArray(data) in the .then(data) try doing this.setState({members: [...this.state.members, data] Commented Apr 4, 2020 at 12:01
  • I think that initialization has not finished yet. My opinion is to use constructor to initialie state and then fill its data either @ your contructor or you componentDidMount Commented Apr 4, 2020 at 12:01

1 Answer 1

1

As Dragos commented, your getExpensesFromUser function returns asynchronous results, so you need to make sure to only call setState once those calls have finished.

const membersRef = firebase.database().ref(`${groupName}/members`);
membersRef.on('value', (data) => {
  const promises = [];
  data.forEach((snapshot) => {
    var memberKey = snapshot.key;
    promises.push(this.getExpensesFromUser(memberKey))
  });

  Promise.all(promises).then((data) => {
    const memberArr = data.map((item) => [memberName, memberKey, memberExpense]);
    this.setState({members: memberArr})
  });
});
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.