Pets Front-End Build the Create Form

Learning Objective: By the end of this lesson, students will be able to create and manage forms within a React application to add new pet entries to a database.

Create a new pet, part 1

Let’s start on the create functionality for adding a new pet to our database. Once again, let’s review the minimum requirements laid out in the intro:

And convert them into a user story:

👤 As a user, I want to be able to enter information into a form and use it to create a new pet entry.

We’ll tackle create functionality in two steps. The first is to build the create form and conditionally render it. The second is to handle the form submission and display the new pet in the UI.

Step 1: Build the create form

In this step, we’ll set up a form where users can input new data for a pet. Typically, forms for creating new entries, like adding a new pet, are placed on separate pages. However, since we’re not using multiple pages or routing in this lesson, we’ll manage the form’s visibility with state. This means we’ll use state to control whether the form is displayed or hidden based on user interactions.

Creating the form component

Inside the components directory, let’s create a new directory called PetForm containing a file called PetForm.jsx.

mkdir src/components/PetForm
touch src/components/PetForm/PetForm.jsx

In PetForm.jsx, create a function component that accepts props as an argument. We’ll return to this file and build out the form in a moment.

Make sure to export the PetForm component at the end of the file.

Add the PetForm component to the App component

Start by importing the PetForm component, then render it on the page:

// src/App.jsx

import { useState, useEffect } from 'react';

import * as petService from './services/petService';

import PetList from './components/PetList/PetList';
import PetDetail from './components/PetDetail/PetDetail';
// Import the new PetForm component
import PetForm from './components/PetForm/PetForm';

const App = () => {
  const [pets, setPets] = useState([]);
  const [selected, setSelected] = useState(null);

  // useEffect and handleSelect code here

  return (
    <>
      <PetList pets={pets} handleSelect={handleSelect} />
      <PetForm />
      <PetDetail selected={selected} />
    </>
  );
};

Create the form

Now that we can see what we’re doing as we work, we’ll build out the PetForm component. We’ll start by creating a controlled form allowing users to input data (name, age, and breed) for a new pet.

We’ll use the useState() hook to manage the form data and the handleChange() function to update the formData state as the user types.

// src/components/PetForm/PetForm.jsx

import { useState } from 'react';

const PetForm = (props) => {
  // formData state to control the form.
  const [formData, setFormData] = useState({
    name: '',
    age: '',
    breed: '',
  });

  // handleChange function to update formData state.
  const handleChange = (evt) => {
    setFormData({ ...formData, [evt.target.name]: evt.target.value });
  };

  // And finally, the form itself.
  return (
    <div>
      <form>
        <label htmlFor="name"> Name </label>
        <input
          id="name"
          name="name"
          value={formData.name}
          onChange={handleChange}
          required
        />
        <label htmlFor="age"> Age </label>
        <input
          id="age"
          name="age"
          value={formData.age}
          onChange={handleChange}
          required
        />
        <label htmlFor="breed"> Breed </label>
        <input
          id="breed"
          name="breed"
          value={formData.breed}
          onChange={handleChange}
        />
        <button type="submit">Add New Pet</button>
      </form>
    </div>
  );
};

export default PetForm;

Conditionally render the form

When the page loads initially, the form should not be open. To accomplish this, we’ll need to create a new state variable inside App.jsx to represent the open or closed form component. We can set the initial value to false to represent it being closed when we first load the App component:

// src/App.jsx

  const [pets, setPets] = useState([]);
  const [selected, setSelected] = useState(null);
  // New state variable to control form visibility.
  const [isFormOpen, setIsFormOpen] = useState(false);

Next, still in App.jsx, create a handleFormView() function to toggle the above state variable. When the function is called, the Boolean value of isFormOpen should change from false to true or vice versa.

We also want to hide the form if it’s open when the user clicks a pet to view its details. We’ll add a call to setIsFormOpen(false) inside the existing handleSelect() function.

// src/App.jsx

  const [pets, setPets] = useState([]);
  const [selected, setSelected] = useState(null);
  const [isFormOpen, setIsFormOpen] = useState(false);

  // useEffect code here.

  const handleSelect = (pet) => {
    setSelected(pet);
    // Close the form if it's open when a new pet is selected.
    setIsFormOpen(false);
  };

  const handleFormView = () => {
    setIsFormOpen(!isFormOpen);
  };

We’ll control whether the form is displayed or hidden when the user clicks a button that will exist inside the PetList component. After creating the function, let’s pass it down to PetList as a prop.

// src/App.jsx
  return (
    <>
      <PetList
        pets={pets}
        handleSelect={handleSelect}
        handleFormView={handleFormView}
      />
      <PetForm />
      <PetDetail selected={selected} />
    </>
  );

The text of the button will need to change depending on whether the form is open. To determine what text the button should display to the user, we’ll need to pass isFormOpen down to PetList.

// src/App.jsx

  return (
    <>
      <PetList
        pets={pets}
        handleSelect={handleSelect}
        handleFormView={handleFormView}
        isFormOpen={isFormOpen}
      />
      <PetForm />
      <PetDetail selected={selected} />
    </>
  );

Finally, in PetList.jsx, we can create our button. It will call handleFormView when clicked, and we’ll also change what text is displayed based on the value of isFormOpen.

// src/components/PetList.jsx

  return (
    <div>
      <h1>Pet List</h1>
      <div>
        {!props.pets.length ? (
          <h2>No Pets Yet!</h2>
        ) : (
          <ul>
            {props.pets.map((pet) => 
              <li 
                key={pet._id}
                style={{ cursor: 'pointer', color: "#646CFF" }}
                onClick={() => props.handleSelect(pet)}
              >
                {pet.name}
              </li>
            )}
          </ul>
        )}
      </div>
      {/* Our new button! */}
      <button onClick={props.handleFormView}>
        {props.isFormOpen ? 'Close Form' : 'New Pet'}
      </button>
    </div>
  );

To complete this step, we’ll need to conditionally render either the PetForm component or the PetDetail based on the value of isFormOpen:

// src/App.jsx

  return (
    <>
      <PetList
        pets={pets}
        handleSelect={handleSelect}
        handleFormView={handleFormView}
        isFormOpen={isFormOpen}
      />
      {isFormOpen ? (
        <PetForm />
      ) : (
        <PetDetail selected={selected} />
      )}
    </>
  );

Test it out in the browser and make sure you can toggle the form open and closed.