Project Appointment Planner, Add Appointment button not working

Hi, have completed this project when I done it as a Functional Components

https://www.codecademy.com/paths/full-stack-engineer-career-path/tracks/fscp-react-part-ii/modules/fscp-appointment-planner/projects/appointment-planner

But I wanted to challenge myself and complete it with Class Components and learn at the same time.

I managed to get it working basically all the way even got the Add New Contact to work but failed at the end. Basically I cant Add Appointment every time I press the button the screen just turns white and when I look in the console I see these following errors:

react-dom.development.js:12314 Uncaught Error: Invalid argument passed as callback. Expected a function. Instead received: [object Object]
index.js:1 The above error occurred in the component:
react-dom.development.js:12314 Uncaught Error: Invalid argument passed as callback. Expected a function. Instead received: [object Object]

Here is my code in my GitHub Repo:

And I uploaded the page on Netlify:
https://hardcore-jones-6f91df.netlify.app

So basically Contact works you need to type a UK number so I put the Regex for it strictly you have to start with
0044 then plus (10 digits)

But the Appointment section doesn’t work and I get the errors above

When I look inside AppointmentsPage I have this:

handleSubmit(e) {
        e.preventDefault();
        const {title, contact, date, time} = this.state;
        this.props.addAppointment(title, contact, date, time)
        this.setState({title: ''});
        this.setState({contact: ''}); 
        this.setState({date: ''}); 
        this.setState({time: ''});
    }

Im sending this down to a Child Component AppointmentForm:

<AppointmentForm 
                        contacts={this.props.contacts}
                        title={this.state.title}
                        updateTitle={this.updateTitle}
                        contact={this.state.contact}
                        updateContact={this.updateContact}
                        date={this.state.date}
                        updateDate={this.updateDate}
                        time={this.state.time}
                        updateTime={this.updateTime}
                        handleSubmit={this.handleSubmit}
                    />

The Child Component from is just a presentational component only does render:

export class AppointmentForm extends React.Component {  
    
    
    render() {
        // const {contacts, title, updateTitle, contact, updateContact, date, updateDate, 
        //     time, updateTime, handleSubmit} = this.props;
        
        const getContactNames = () => this.props.contacts.map((contact) => contact.name);
        const getTodayString = () => {
            const [month, day, year] = new Date().toLocaleDateString("en-US").split('/');
            return `${year}-${month.padStart(2, "0")}-${day.padStart(2, "0")}`;
        }
        return (
            <form onSubmit={this.props.handleSubmit}>
            <label>
                <input 
                    name="title"
                    type="text"
                    value={this.props.title}
                    onChange={(e) => this.props.updateTitle(e.target.value)}
                    required
                    placeholder="Appointment Title"
                />
            </label>
            <br />
            <label>
                <ContactPicker 
                    name="contact"
                    value={this.props.contact}
                    contacts={getContactNames()}
                    onChange={(e) => this.props.updateContact(e.target.value)}
                    placeholder="Appointment with"
                />
            </label>
            <br />
// the rest is just more or else the same off the first input

And this is the full code for AppointmentsPage:

import React from 'react';
import { AppointmentForm } from "../../components/appointmentForm/AppointmentForm";
import { TileList } from "../../components/tileList/TileList";

export class AppointmentsPage extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            title: '',
            contact: this.props.contacts > 0 ? this.props.contacts[0].name : '',
            date: '',
            time: ''
        }
        this.updateTitle = this.updateTitle.bind(this);
        this.updateContact = this.updateContact.bind(this);
        this.updateDate = this.updateDate.bind(this);
        this.updateTime = this.updateTime.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
    }
    // componentDidMount() {
    //     this.setState({contact: this.props.contacts > 0 ? this.props.contacts[0]['name'] : ''})
    // }
    updateTitle(nextState) {
        this.setState({title: nextState})
    }
    updateContact(nextState) {
        this.setState({contact: nextState})
    }
    updateDate(nextState) {
        this.setState({date: nextState})
    }
    updateTime(nextState) {
        this.setState({time: nextState})
    }
    componentDidMount() {
        this.updateTitle();         
        this.updateContact();
        this.updateDate();                 
        this.updateTime();
    }
    componentDidUpdate(prevProps, prevState, snapShot) {
        console.log("updated", prevProps);
    }
    handleSubmit(e) {
        console.log('What happened');
        console.log('What happened' + e);
        console.log('What happened');
        e.preventDefault();
        const {title, contact, date, time} = this.state;
        this.props.addAppointment(title, contact, date, time)
        this.setState({title: ''});
        this.setState({contact: ''}); 
        this.setState({date: ''}); 
        this.setState({time: ''});
    }
    render() {
        return (
            <div>
                <section>
                    <h2>Add Appointment</h2>
                    <AppointmentForm 
                        contacts={this.props.contacts}
                        title={this.state.title}
                        updateTitle={this.updateTitle}
                        contact={this.state.contact}
                        updateContact={this.updateContact}
                        date={this.state.date}
                        updateDate={this.updateDate}
                        time={this.state.time}
                        updateTime={this.updateTime}
                        handleSubmit={this.handleSubmit}
                    />        
                </section>
                <hr />
                <section>
                    <h2>Appointments</h2>
                    <TileList tiles={this.props.appointments} />
                </section>
            </div>
        )
    }
}

And Here is the App.js which is the Ultimate Parent:

import React from "react";
import { Switch, Route, Redirect, NavLink, BrowserRouter as Router } from "react-router-dom";

import { AppointmentsPage } from "./containers/appointmentsPage/AppointmentsPage";
import { ContactsPage } from "./containers/contactsPage/ContactsPage";

const ROUTES = {
  CONTACTS: "/contacts",
  APPOINTMENTS: "/appointments",
};

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      contacts: [],
      appointments: []
    }
    this.addContact = this.addContact.bind(this);
    this.addAppointment = this.addAppointment.bind(this);
  }

  addContact(name, phone, email) {
    this.setState((prevState) => ({
      ...prevState,
      contacts: [...prevState.contacts,
        {
          name: name,
          phone: phone,
          email: email
        }
      ]
    }))
    
  }

  addAppointment(title, contact, date, time) {
    this.setState((prevState) => ({
      ...prevState,
      appointments: [...prevState.appointments,
      {
        title: title,
        contact: contact,
        date: date,
        time: time
      }]
    }))
  }

  

  render() {

    return (
      <Router>
        <nav>
          <NavLink to={ROUTES.CONTACTS} activeClassName="active">
            Contacts
          </NavLink>
          <NavLink to={ROUTES.APPOINTMENTS} activeClassName="active">
            Appointments
          </NavLink>
        </nav>
        <main>
          <Switch>
            <Route exact path="/">
              <Redirect to={ROUTES.CONTACTS} />
            </Route>
            <Route path={ROUTES.CONTACTS}>
              <ContactsPage contacts={this.state.contacts} addContact={this.addContact} />
            </Route>
            <Route path={ROUTES.APPOINTMENTS}>
              <AppointmentsPage
                appointments={this.state.appointments}
                addAppointment={this.addAppointment}
                contacts={this.state.contacts}
              />
            </Route>
          </Switch>
        </main>
      </Router>
    );
    }
}

export default App;

More over I have to admit I am clueless when it comes to componentDidUpdate and ComponentDidMount need to have more practise with them if you lot know where to learn it in more depth please share

Hi Mohamed,

I didn’t go very much into details with this, so I cannot offer a solution. Just two things I noticed. I was stumbling especially upon your component ‘AppointmentForm’.

You’re using ‘props’ here:

and here:

But your component doesn’t have a constructor which receives these props.

Also, from what I remember, the function definitions shouldn’t be located within the render() function:

Question do I have to put for every component even the stateless presentational components into a constructor to receive the incoming props?

No problem I can I make it into a method then bind it in the constructor

These props I made these props.update from this AppointmentForms Parent component the AppoinmentsPage because I wanted to send down the this.setState down and use it for that input so I thought of doing it this way by putting this.setState into a method then sending the method down
As I have it on the AppointmentsPage Component here:

updateTitle(nextState) {
        this.setState({title: nextState})
    } // updateContact() etc are all done similarly 

No, but from what I see, that component is not a stateless component:

Yes, and in that component you have a constructor and you’re receiving the props. Both are stateful components the way they are currently built.

I took those two function definitions out from my render() method and put them right above the render() method and I made a constructor() method where I binded both of them So i got it to work now I think I need more in depth learning about componentDidMount and componentDidUpdate and when and how to use them because this is where I feel like I am lacking the most

AppointmentForm.js updated

export class AppointmentForm extends React.Component {  
    constructor(props) {
        super(props);
        this.getTodayString = this.getTodayString.bind(this);
        this.getContactNames = this.getContactNames.bind(this);
    }
    getContactNames() {
        return this.props.contacts.map((contact) => contact.name);
    }
    getTodayString() {
        const [month, day, year] = new Date().toLocaleDateString("en-US").split('/');
        return `${year}-${month.padStart(2, "0")}-${day.padStart(2, "0")}`;
    }
    render() {

        const {title, updateTitle, contact, updateContact, date, updateDate, 
            time, updateTime, handleSubmit} = this.props;
        return (
            <form onSubmit={handleSubmit}>
            <label>
                <input 
                    name="title"
                    type="text"
                    value={title}
                    onChange={(e) => updateTitle(e.target.value)}
                    required
                    placeholder="Appointment Title"
                />
            </label>
            <br />
            <label>
                <ContactPicker 
                    name="contact"
                    value={contact}
                    contacts={this.getContactNames()}
                    onChange={(e) => updateContact(e.target.value)}
                    placeholder="Appointment with"
                />
            </label>
            <br />
            <label>
                <input 
                    name="date"
                    type="date"
                    value={date}
                    min={this.getTodayString()}
                    onChange={(e) => updateDate(e.target.value)}
                    requried
                />
            </label>
            <br />
            <label>
                <input
                    name="time"
                    type="time"
                    value={time}
                    onChange={(e) => updateTime(e.target.value)}
                    required
                />
            </label>
            <br />
            <input type="submit" value="Add Appointment" />
        </form>
        )
    }
}

And the AppointmentsPage.js componentDidMount and componentDidUpdate I am totally clueless

import React from 'react';
import { AppointmentForm } from "../../components/appointmentForm/AppointmentForm";
import { TileList } from "../../components/tileList/TileList";

export class AppointmentsPage extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            title: '',
            contact: this.props.contacts > 0 ? this.props.contacts[0].name : '',
            date: '',
            time: ''
        }
        this.updateTitle = this.updateTitle.bind(this);
        this.updateContact = this.updateContact.bind(this);
        this.updateDate = this.updateDate.bind(this);
        this.updateTime = this.updateTime.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
    }
    // componentDidMount() {
    //     this.setState({contact: this.props.contacts > 0 ? this.props.contacts[0]['name'] : ''})
    // }
    updateTitle(nextState) {
        this.setState({title: nextState})
    }
    updateContact(nextState) {
        this.setState({contact: nextState})
    }
    updateDate(nextState) {
        this.setState({date: nextState})
    }
    updateTime(nextState) {
        this.setState({time: nextState})
    }
    componentDidMount() {/// IAM CLULESS IN WHAT THE HECK I AM DOING LOL
        this.updateTitle();     /// IAM CLULESS IN WHAT THE HECK I AM DOING LOL    
        this.updateContact();/// IAM CLULESS IN WHAT THE HECK I AM DOING LOL
        this.updateDate();/// IAM CLULESS IN WHAT THE HECK I AM DOING LOL
        this.updateTime();/// IAM CLULESS IN WHAT THE HECK I AM DOING LOL
    }
    componentDidUpdate(prevProps, prevState, snapShot) {
        console.log("updated", prevProps); /// IAM CLULESS IN WHAT THE HECK I AM DOING LOL
    }
    handleSubmit(e) {
        console.log('What happened');
        console.log('What happened' + e);
        console.log('What happened');
        e.preventDefault();
        const {title, contact, date, time} = this.state;
        this.props.addAppointment(title, contact, date, time)
        this.setState({title: ''});
        this.setState({contact: ''}); 
        this.setState({date: ''}); 
        this.setState({time: ''});
    }
    render() {
        return (
            <div>
                <section>
                    <h2>Add Appointment</h2>
                    <AppointmentForm 
                        contacts={this.props.contacts}
                        title={this.state.title}
                        updateTitle={this.updateTitle}
                        contact={this.state.contact}
                        updateContact={this.updateContact}
                        date={this.state.date}
                        updateDate={this.updateDate}
                        time={this.state.time}
                        updateTime={this.updateTime}
                        handleSubmit={this.handleSubmit}
                    />        
                </section>
                <hr />
                <section>
                    <h2>Appointments</h2>
                    <TileList tiles={this.props.appointments} />
                </section>
            </div>
        )
    }
}