How do i correctly return in a map function

Viewed 2308

I have a strange error. I have a FlatList that renders the items from the mapStateToProps which returns the classes resulted from a firebase fetch. In that _.map(state.classes... i return the class conditionally but if i dont'return something in else i get an error from inside the flatlist that is complaining that a prop is missing but if i return an empty object instead i dont get any errors and the render is as expected. The thing is that i want to know if this is the normal behavior. Do i need to return something? why does it complain that a prop is missing if i dont return that object at all? Thanks in advance, Vlad!

import React, { Component } from "react";
import {
    Text,
    View,
    FlatList,
    NativeModules,
    LayoutAnimation,
    Alert,
    Modal,
    TouchableHighlight
} from "react-native";
import _ from 'lodash';

import { Icon, Container } from 'native-base';;
import { CardSection, Confirm } from '../../common/index'
import { connect } from 'react-redux';
import { fetchClasses, fetchStudents } from '../../../actions/index';
import { List, ListItem, Header } from "react-native-elements"
import Icon1 from 'react-native-vector-icons/FontAwesome';

const { UIManager } = NativeModules
UIManager.setLayoutAnimationEnabledExperimental
    && UIManager.setLayoutAnimationEnabledExperimental(true)

class Home extends Component {


    constructor() {
        super();
        this.state = {
            selectedUid: null,
            isModalVisible1: false,
            currentClass: {},
            currentStudent: {},
            months: ['Ianuarie', 'Februarie', 'Martie', 'Aprilie', 'Mai', 'Iunie', 'Iulie', 'August', 'Septembrie', 'Octombrie', 'Noiembrie', 'Decembrie']

        }
    }
    componentWillMount() {
        this.props.fetchClasses();
        this.props.fetchStudents();

    }
    componentDidUpdate() {
        LayoutAnimation.spring();
    }
    static navigationOptions = {

        header: null
    }

    render() {
        return (
            <Container style={{ marginBottom: 5 }}>
                <Header
                    backgroundColor={'#1E6EC7'}
                    placement="left"
                    leftComponent={{ icon: 'menu', color: '#fff' }}
                    centerComponent={{ text: 'Programul Zilei', style: { color: '#fff', fontWeight: 'bold', fontSize: 22 } }}
                    rightComponent={<Icon name="ios-add" style={{ color: 'white' }} onPress={() => this.props.navigation.navigate('AddClass', this.props.students)} />}
                />
                <List>
                    <FlatList
                        data={this.props.classes}
                        keyExtractor={(item, index) => `${index}`}
                        extraData={this.state}
                        renderItem={({ item }) => {
                            let wantedEmployee = null
                            if (this.props.students !== []) {
                                this.props.students.forEach(student => {
                                    if (student.uid === item.studentUid)
                                        wantedEmployee = student;
                                });
                                if (wantedEmployee !== null)
                                    return <View><ListItem
                                        leftIcon={<Icon1 name="times" size={24} style={{ paddingRight: 10, color: 'red' }} onPress={() => {
                                            this.setState({ currentStudent: wantedEmployee })
                                            this.setState({ currentClass: item })
                                            this.setState({ isModalVisible1: true })
                                        }} />}
                                        onPress={() => {
                                            if (this.state.selectedUid !== item.uid)
                                                this.setState({ selectedUid: item.uid })
                                            else
                                                this.setState({ selectedUid: null })
                                        }}
                                        title={`${item.hour}:${item.minutes}: ${wantedEmployee.nume}`}
                                        subtitle={item.year}
                                        rightIcon={this.state.selectedUid === item.uid ? <Icon name="md-arrow-dropdown" /> : <Icon name="md-arrow-dropright" />}
                                    />
                                        {this.state.selectedUid === item.uid ?
                                            <View><CardSection><Text>Nume: <Text style={{ fontWeight: 'bold' }}>{wantedEmployee.nume}</Text></Text></CardSection>
                                                <CardSection><Text>Numar de Telefon: <Text style={{ fontWeight: 'bold' }}>{wantedEmployee.phone}</Text></Text></CardSection>
                                                <CardSection><Text>CNP: <Text style={{ fontWeight: 'bold' }}>{wantedEmployee.cnp}</Text></Text></CardSection>
                                                <CardSection><Text>Numar Registru: <Text style={{ fontWeight: 'bold' }}>{wantedEmployee.registru}</Text></Text></CardSection>
                                                <CardSection><Text>Serie: <Text style={{ fontWeight: 'bold' }}>{wantedEmployee.serie}</Text></Text></CardSection></View>
                                            : null}

                                    </View>
                            }
                        }
                        }
                    />
                </List>
                <Confirm visible={this.state.isModalVisible1} onDecline={() => this.setState({ isModalVisible1: false })}>
                    Esti sigur ca vrei sa stergi sedinta de pe <Text style={{ fontWeight: 'bold' }}>{this.state.currentClass.day} {this.state.months[this.state.currentClass.month]} {this.state.currentClass.year}</Text> cu <Text style={{ fontWeight: 'bold' }}>{this.state.currentStudent.nume}</Text>?
            </Confirm>
            </Container>



        );
    }
}
const mapStateToProps = (state) => {
    function compare(a, b) {
        if (a.nume < b.nume)
            return -1;
        if (a.nume > b.nume)
            return 1;
        return 0;
    }
    function compareClasses(a, b) {
        if (a.hour < b.hour)
            return -1;
        if (a.hour > b.hour)
            return 1;
        return 0;
    }
    const date = new Date();
    const year1 = date.getFullYear();
    const month1 = date.getMonth();
    const day1 = date.getDate();
    const classes = _.map(state.classes, (val, uid) => {
        const { year, month, day, hour, minutes, studentUid } = val;

        if (year === year1 && month === month1 && day1 === day)
            return { year, month, day, hour, minutes, studentUid, uid };
        else
            return {}
    });
    const students = _.map(state.studentsFetch, (val, uid) => {
        return { ...val, uid };

    });
    classes.sort(compareClasses)
    students.sort(compare)
    return { classes, students };
}
export default connect(mapStateToProps, { fetchClasses, fetchStudents })(Home);
2 Answers

It seems like what you're trying to do is filter out data from your array. One solution may be using the filter method rather than the map method since map expects something to be returned:

Produces a new array of values by mapping each value in list through a transformation function

If you don't return anything explicitly from the mapping function, it will return undefined, so the resulting list may look like:

[
    { year: 2018, month: 9, day: 17, hour: 16, minutes: 6, studentUid: 'ABC123', uid: 'abc456'},
    undefined,
    { year: 2018, month: 9, day: 01, hour: 16, minutes: 0, studentUid: 'DEF567', uid: 'def890'},
    // ...
]

Later, your compareClasses function will try to call a.hour where a is undefined, leading to (undefined).hour, which will throw an error with:

TypeError: Cannot read property 'hour' of undefined

So what you probably want is to remove the undefined values, which can be accomplished a few ways. One way, probably the clearest, would be to move your if () statement to a .pickBy (which works like .filter for objects), then move the code that extracts the properties you want to a subsequent .map.

const classes = _(state.classes)
    .pickBy((val, uid) => (
        val.year === year1 &&
        val.month === month1 &&
        val.day === day1
    ))
    .map((val, uid) =>
      ({ uid, ..._.pick(val, 'year', 'month', 'day', 'hour', 'minutes', 'studentUid')})
    )
    .value();

(Note: Personally, I'm a fan of using lodash's sequence constructor to wrap the object when I'm using multiple functions, so I've done that here with _(state.classes), but you can also nest the functions, _.map(_.pickBy(state.classes, (val, key)=>...), (val, key) => ...) ).

Related