Search code examples
reactjsreduxreact-reduxgoogle-cloud-firestorereact-redux-firebase

How to transform async data in mapStateToProps?


  • I am using firestoreConnect() to sync my data from firestore with redux.

  • I am getting an array of employee objects back from firestore via state.firestore.ordered

  • an employee is shaped like this:

    {
      id: 'XtVe567...',
      name: {
        first: 'John',
        last: 'Doe'
      }
    }
    
  • In mapStateToProps I need to transform the shape of this data before I return it and pass it into a component via props

    function mapStateToProps(state) {
    
      const schedules = state.firestore.ordered.employees
        .map(employee => ({
          id: employee.id,
          name: {
            first: employee.name.first,
            last: employee.name.last
          },
          schedule: [null, null, null, null, null, null, null]
        }));
    
      const rota = {
       id: null,
       date: moment(),
       notes: null,
       events: [null, null, null, null, null, null, null],
       published: null,
       body: schedules
      };
    
      return {rota}
    }
    
  • This results in: TypeError: Cannot read property 'map' of undefined

  • I know this is because fetching data is asynchronous and at the point in time when I map over state.firestore.ordered.employees the employees property === undefined which isn't iterable

So, my question is how DO you make this sort of data transformation with async data? Is there a way to some how pause the execution of mapStateToProps and wait for the data to be returned BEFORE mapping over it?

Thanks and sorry in advance if this question is unintelligible, it's my first time posting.


Solution

  • To quickly make it work you could just do something like:

    function mapStateToProps(state) {
    
        const employees = state.firestore.ordered.employees;
    
        const schedules = employees
            ? employees.map(employee => ({
                id: employee.id,
                name: {
                    first: employee.name.first,
                    last: employee.name.last
                },
                schedule: [null, null, null, null, null, null, null]
            }))
            : undefined;
    
        const rota = {
            id: null,
            date: moment(),
            notes: null,
            events: [null, null, null, null, null, null, null],
            published: null,
            body: schedules
        };
    
        return { rota }
    }
    

    Then, in you component, you can check for schedules attribute of rota object and if it's still undefined, render something to indicate that data is not loaded yet.

    Still, putting such a complex logic into mapStateToProps is an antipattern for me. You can use selector pattern to make your code more organized.