Search code examples
reactjsreact-reduxreact-redux-firebase

How to retrieve and load data from a Firestore subcollection using React-Redux-Firebase?


I want to fetch a document called settings from a Firestore subcollection, then load it into my local component (and Redux store) as a variable named settings with the following value:

settings: {
  name: 'Waldo Garply',
  email: '[email protected]',
  mobile: '555-789-1234',
  timestamp: '1546304499032',
}

Instead, my app fails to compile and I get the following error message in my console:

console.error

Uncaught TypeError: Cannot read property 'settings' of undefined at Function.mapStateToProps [as mapToProps]

What am I doing wrong and how can I achieve my expected behavior?

I am storing my Firestore data as follows.

Firestore
.
├── users
|   ├── OGk02kJbQUesTeVhTrLBnERSxrfm
|   |   ├── settings
|   |   |   ├── VrxDnSxpUw6wgX0n9c1FbapmLaLa
|   |   |   |   ├── name: Waldo Garply
|   |   |   |   └── timestamp: 1546304499030
|   |   |   ├── cGVHxSkU3Lcb9WAYWjnJKcLOTYf8
|   |   |   |   ├── name: Waldo Garply
|   |   |   |   ├── email: [email protected]
|   |   |   |   └── timestamp: 1546304499031
|   |   |   ├── qoDYG2xloEvUUhGQyF9zXy9MTMIq
|   |   |   |   ├── name: Waldo Garply
|   |   |   |   ├── email: [email protected]
|   |   |   |   ├── mobile: 555-789-1234
|   |   |   |   └── timestamp: 1546304499032

Notice, I am storing a unique snapshot of all the settings values (including a timestamp) each time any of the settings values changes; I then fetch the latest setting (sorted by timestamp) and load it. So I am needing an automatic listener on the settings object.

I am using the following code in my component (called DetailsTab.js) to try connect to Firestore to fetch the data then load it as a settings variable into my component and Redux store.

DetailsTab.js
function mapStateToProps( state ) {
  console.log('state\n', state);
  return {
    user: state.auth.user,
    // attempted all the following individually
    settings: state.firestore.data.users.settings, // throws error
    settings: state.firestore.ordered.users.settings, // throws error
    settings: state.firestore.ordered.users[0] // throws error
    settings: state.firestore.ordered.users // returns 'users' object
  }
}

export default compose(
  withStyles(styles, { withTheme: true }),
  connect(mapStateToProps, mapDispatchToProps),
  firestoreConnect(props => {
    return [
      {
        collection: 'users',
        doc: props.user.data.uid,
        subcollections: [
          {
            collection: 'settings',
            limit: 1,
            orderBy: ['timestamp', 'desc',],
            storeAs: 'settings',
          },
        ],
      },
    ];
  })
)(DetailsTab)

The following is how the data appears when my console logs it.

console.log
state
└── firestore
    ├── data
    |   └── users
    |       └── OGk02kJbQUesTeVhTrLBnERSxrfm
    |           └── settings
    |               └── qoDYG2xloEvUUhGQyF9zXy9MTMIq
    |                   ├── name: Waldo Garply
    |                   ├── email: [email protected]
    |                   ├── mobile: 555-789-1234
    |                   └── timestamp: 1546304499032
    ├── ordered
    |   └── users [Array(1)]
    |       └── 0
    |           ├── id: OGk02kJbQUesTeVhTrLBnERSxrfm
    |           └── settings [Array(1)]
    |               └── 0
    |                   ├── id: qoDYG2xloEvUUhGQyF9zXy9MTMIq
    |                   ├── name: Waldo Garply
    |                   ├── email: [email protected]
    |                   ├── mobile: 555-789-1234
    |                   └── timestamp: 1546304499032

Solution

  • The solution was to add the appropriate error guards as follows.

    function mapStateToProps( state ) {
      console.log('state\n', state);
      const settings = state.firestore.ordered.users
                    && state.firestore.ordered.users[0]
                    && state.firestore.ordered.users[0].settings
                    && state.firestore.ordered.users[0].settings[0];
      return {
        user: state.auth.user,
        settings,
      }
    }