0

I have been working on an event signup page for a school club I am in, and I cannot seem to have my program read the contents of my array, and put each one in a ListItem, which goes in a List for the life of me.

When I call this function, the boolean expression this.state.events.length !== 0 evaluates to False, however console.log(this.state.events) shows the array is not empty.

What might be causing this and how do you propose I go about fixing this.

Side note: I would love some criticism of my code. I just started with JS, React, and MaterialUI and would like to improve.

class EventSelector extends Component {

    constructor(props){
        super(props);

        this.state = {
            events: []
        }

    }

    componentDidMount = () => {

        var lst = [] // this is a list of events s.t. {key: "event_name"}

        const eventsRef = db
                        .collection('events'); // this is a reference to the events collection

        const offeredRef = eventsRef
                        .where('allowRegistration', '==', true)
                        .get() // this contains all docs which allow registration.
                        .then((querySnapshot) => {
                            querySnapshot.forEach((doc) => { // for each document, create key value pairs
                                lst.push({key: doc.data().name, id: doc.id})
                            })
                        })
        // console.log(lst)
        this.setState({ events: lst })
        return true;

    }

    EventList = (events) => {

        const { classes } = this.props;

        return(
        
            <div>
                <Grid container item 
                        direction='column' 
                        alignItems='center' 
                        justify='center' 
                        style={{ maxHeight: '70vh', maxWidth: '50vw' }} 
                        spacing={5}>

                        <Grid item>
                            <h1 className={classes.h1}>Upcoming Events</h1>
                            <h2 className={classes.h2}>Please select an event to sign up</h2>
                        </Grid>

                    <Grid container item direction='row' justify='center' spacing={5}>

                        <List component='b' subheader={<ListSubheader componenet='b'>Upcomming Events</ListSubheader>}>
                            {events.map(( {key , id} ) => {
                                // console.log(key)
                                return (
                                    <div key={id}>
                                        <ListItem button>
                                            <ListItemText inset primary={key}/>
                                        </ListItem>
                                    </div>
                                );
                            }) }

                        </List>

                    </Grid>   

                </Grid>
            </div>
        );
    }


    // }

    render = () => {

        // const { classes, lists } = this.props;
        const { classes } = this.props;

        console.log(this.state.events)
        var obj = Object.assign({}, this.state.events)
        console.log(JSON.stringify(obj))

        return (this.state.events.length !== 0 ? <h1>{JSON.stringify(this.state.events)}</h1> : <h2>Loading</h2>)


    
    }
}

export default withStyles(styles)(EventSelector);

console output with Object.assign()

console output without Object.assign()

Joshua Reisbord
  • 111
  • 2
  • 7
  • The console will print a live reference of an object, so if it changes after it was logged the console will reflect that change. That's happening here `console.log(this.state.events)` logs the `lst` array, but it doesn't have any values in it (it's an empty array) when it's logged. That's because the code which populates it `.then((querySnapshot) => {` runs asynchronously, meaning that you're setting your state before `lst` has any items in it. Try changing your code so that you set your state below the `.forEach()` method: `this.setState({ events: lst })` – Nick Parsons Feb 20 '21 at 23:12
  • Prefer functional components over class components, it is currently being recommended by default as you can use React hooks only along with functional components. – richardaum Feb 20 '21 at 23:32
  • There is no difference between your console ouputs with or without Object.assign. – richardaum Feb 20 '21 at 23:37

1 Answers1

0

As far as I can see, there is nothing wrong with your code. One interesting thing about React, is that all your state changes will result into a new call to the render method.

  state = { events: [] };

  componentDidMount() {
    // const lst = ...
    this.setState({ events: lst });
    // no need return here
  };

  render() {
    console.log(this.state.events);
  }

First time, it will print [] because your state is initialized this way. After the component is mounted, events will be print just like it was filled.

Another way to write this code is using a functional component:

import { useState } from 'react';

const EventSelector = () => {
  const [events, setEvents] = useState([]);

  useEffect(() => {
    // const lst = ...
    setEvents(lst);
  }, []);

  return events.length !== 0 ? (
    <h1>{JSON.stringify(this.state.events)}</h1>
  ) : (
    <h2>Loading</h2>
  );
}

IMHO, functional components are better to read. This code works just as yours, the same behavior is expected.

richardaum
  • 6,651
  • 12
  • 48
  • 64