bloppit
bloppit

Reputation: 641

How can I work with my list after using orderByChild?

I'm building an app that in which users can make posts and then vote on them. I want to be able to request the list of posts from firebase and order them by vote count. How can I do this?

My data is structure like this:

enter image description here

I'm attempting to fire this action:

export const fetchPostsByHottest = () => {
  return (dispatch) => {
    firebase.database().ref('/social/posts')
      .orderByChild('vote_count')
      .on('value', snapshot => {
        dispatch({ type: HOT_POSTS_FETCH_SUCCESS, payload: snapshot.val() });
      });
  };
};

It just comes back in the normal order, by push key.

I've been told that snapshot.val() converts the snapshot into a regular JS object and kills any order.

This is my reducer:

import {
  HOT_POSTS_FETCH_SUCCESS,
} from '../actions/FeedActions/types';

const INITIAL_STATE = {
};

export default (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case HOT_POSTS_FETCH_SUCCESS:
      return action.payload;
    default:
      return state;
  }
};

I then try to map the state to props in my feed component:

const mapStateToProps = state => {
  const posts = _.map(state.hotFeed, (val, uid) => {
    return { ...val, uid };
  });

  return { posts: posts.reverse() };
};

I do it like this as I need to get both the Value AND the UID from each of the post objects.

If I try and do turn it into an array in the action like this:

export const fetchPostsByHottest = () => {
  return (dispatch) => {
    firebase.database().ref('/social/posts')
      .orderByChild('vote_count')
      .on('value', snapshot => {
        const nodes = [];
        snapshot.forEach(child => nodes.push(child.val()));

        console.log(nodes);
        dispatch({ type: HOT_POSTS_FETCH_SUCCESS, payload: nodes });
      });
  };
};

Then it will only bring me back one of the posts and it won't give me the UID either.

How can I work around this? :S

Thank you!

Matt

Upvotes: 1

Views: 559

Answers (1)

cartant
cartant

Reputation: 58400

The query in your last snippet should be correct. There is no reason it should return only a single post. However, it's possible that the snapshot's forEach is being short-circuited:

The callback can return true to cancel further enumeration.

push returns the new length and it's possible that forEach is testing for any truthy value - not just true - and is cancelling further enumeration. Use a block to ensure undefined is returned.

Also, you can obtain the key using the child's key property.

firebase.database().ref('/social/posts')
  .orderByChild('vote_count')
  .once('value', snapshot => {

    const nodes = [];
    snapshot.forEach(child => { nodes.push({ ...child.val(), key: child.key }); });

    console.log(nodes);
    dispatch({ type: HOT_POSTS_FETCH_SUCCESS, payload: nodes });
  });

Also, for your use case, once seems more appropriate than on.

Upvotes: 3

Related Questions