Appointment Planner: Warning: Functions are not valid as a React child

https://www.codecademy.com/projects/practice/appointment-planner

Offline I completed a working version of Appointment Planner using react@18.2.0, react-router-dom@6.3.0. I am now doing a version from memory. It is fully functional with one exception, i.e., in the AppointmentForm the ContactPicker fails to render. The app is compiling without error. See screen shot below code. You can see I can add a contact and make an appointment, but cannot associate the appointment with a contact. The select element does not display. I have verified getContacts() is called via console log.

Even when I copy the solution code for those two components into the source files, I still get the following console error.

VM27 react_devtools_backend.js:4026 Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.
at ContactPicker
at label
at form
at AppointmentForm (http://localhost:8889/static/js/bundle.js:187:5)
at section
at Appointments (http://localhost:8889/static/js/bundle.js:383:5)
at Routes (http://localhost:8889/static/js/bundle.js:40920:5)
at main
at App (http://localhost:8889/static/js/bundle.js:39:82)
at Router (http://localhost:8889/static/js/bundle.js:40853:15)
at BrowserRouter (http://localhost:8889/static/js/bundle.js:39662:5)

Source Code:

import React from 'react'

import ContactPicker  from "./contactPicker"

const AppointmentForm = ({
  contacts,
  title,
  setTitle,
  contact,
  setContact,
  date,
  setDate,
  time,
  setTime,
  handleSubmit,
  alert,
}) => {
  const getTodayString = () => {
    const [month, day, year] = new Date()
      .toLocaleDateString("en-US")
      .split("/");
    return `${year}-${month.padStart(2, "0")}-${day.padStart(2, "0")}`;
  };

  const getContactNames = () => {
    return contacts.map((contact) => contact.name);
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        <input
          type="text"
          name="title"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
          required
          placeholder="Appointment Title"
        />
      </label>
      <br />
      <label>
        <ContactPicker
          name="contact"
          value={contact}
          contacts={getContactNames()}
          onChange={(e) => setContact(e.target.value)}
          placeholder="Appointment With"
        />
      </label>
      <br />
      <label>
        <input
          type="date"
          name="date"
          min={getTodayString()}
          value={date}
          onChange={(e) => setDate(e.target.value)}
          required
        />
      </label>
      <br />
      <label>
        <input
          type="time"
          name="time"
          value={time}
          onChange={(e) => setTime(e.target.value)}
          required
        />
      </label>
      <button type="submit" disabled={alert ? true : false} >Add Appointment</button> 
      <h2 className="alert">{alert}</h2>
    </form>
  );
};

export default AppointmentForm
import React from 'react'

const ContactPicker = () => ({ name, onChange, contacts }) => {
  return (
    <select name={name} onChange={onChange}>
      <option value={""} key={-1} selected="selected">
        No Contact Selected
      </option>
      {contacts.map((contact) => {
        return (
          <option value={contact} key={contact}>
            {contact}
          </option>
        );
      })}
    </select>
  );
};

export default ContactPicker

**


**

I believe your issue is from the below code. You are calling the function instead of passing the function as a prop. Remove the () from getContactNames and you should be fine. Looks like you did it with the getTodayString as well in the input. Fixing those two issues should get you going.

Thanks for the reply. That code is from the solution. What you are suggesting makes sense. But it did not change the result I am getting the same error in the code getContanctNames plus one for getTodayString:

Invalid value for prop min on input tag. Either remove it from the element, or pass a string or number value to keep it in the DOM

Additionally the AppointmentForm should be passing getContactNames as a prop as you stated to ContactPicker. But that component does not have that as a property. It is not destructed from props. Instead ContactPicker appears to generate a list of contact objects rather than a list of names.

I really do not understand how this part of the app works. Again thanks for the response.

You are right. Can you post your repo so I can look at the project and load it? That might help.

I think I see now…You may have the calls backward. In your AppointmentForm.js you should have the contact picker set like this:

<ContactPicker
          name="contact"
          value={contact}
          contacts={getContactNames()}
          onChange={setContact}
          placeholder="Appointment With"
        />

Then in your ContactPicker.js

export const ContactPicker = ({ contact, contacts, onChange }) => {
  return (
    <select
      name="contactPicker"
      id="contactPicker"
      value={contact}
      onChange={(e) => onChange(e.target.value)}
    >

My previous comment was totally incorrect. But this should set you straight.

This is a step forward, because the label is being displayed as a dropdown but the dropdown does not have the names. I will get this posted on GitHub.

Thanks to @1moregame, this problem is solved (see the following post)

Here is the repository: You will find appointment related stuff in src/components/appointments

I solved from your suggestions like this: (it is strange this is not a problem with the my first version and both versions are using react@18.2.0, react-router-dom@6.3.0 9 (so same exact environment). This should also be a problem in the solution version. It appears that getContactNames = {getContactNames()} in the my first version and the solution version is interpreted as an assignment. But in my 2nd version it is interpreted as an illegal child of a component.

In AppointmentForm:

      <ContactPicker
          name="contact"
          value={contact}
          getContactNames={getContactNames}
          onChange={setContact}
          placeholder="Appointment With"
        />
      </label>

In ContactPicker:

const ContactPicker = ({ contact, contacts, onChange, getContactNames }) => {
  const names = getContactNames();
  return (
    <select name="contactPicker" id="contactPicker" value={contact} onChange={(e) => onChange(e.target.value)}>
      <option value={''} key={-1} selected="selected">
        {' '}
        No Contact Selected
      </option>
      {names.map((name) => {
        return (
          <option value={name} key={name}>
            {name}
          </option>
        );
      })}
    </select>
  );
};

export default ContactPicker;

Nice work! Sorry if I wasn’t clear that you should still map through the options to get the names to display.

My dunce-ness. But I did eventually realized that is what you meant. Again, you put me on the path where I could see the pattern.

1 Like