← Back

React.js + Next.js · Module 3. Forms and Effects · Lesson 3 · Point 3

React Form as a Component

In Point 2, one component displayed the form and processed its data.

Now we will divide the work between two components:

  • OrderForm displays the fields and collects their values.
  • App decides what to do with the submitted values.

We will use normal named function declarations. We will not use arrow functions for our function implementations, and we will not destructure props.

All examples continue the Pizza Palace online-ordering project.


REACT · 01

1. Why Put a Form in Its Own Component?

A React component should have one clear job.

For our Pizza Palace application:

  • OrderForm collects the customer's information.
  • App processes the order.

The parent may later save the order, send it to an API, show a confirmation, or update state. The form does not need to know those details.

App (parent)
│
│ gives a function to OrderForm
▼
OrderForm (child)
│
│ collects the customer's name
│ calls the function from App
▼
App receives the name
│
▼
App decides what happens next

This separation makes the form easier to read, reuse, and test.


REACT · 02

2. Remember How Props Work

Props let a parent component give information to a child component.

<Greeting name="Nikita" />

The child receives a props object:

interface GreetingProps {
  name: string;
}

export default function Greeting(props: GreetingProps) {
  return <h2>Hello, {props.name}!</h2>;
}

Browser Result

Hello, Nikita!

The child reads the name with props.name.

Read this line from left to right:

export default function Greeting(props: GreetingProps)
  • Greeting is the component.
  • props is the object it receives.
  • GreetingProps describes that object for TypeScript.

We use props.name. We do not destructure props in this lesson.


REACT · 03

3. Pass a Function Through Props

Props can also contain a function.

First, the parent creates a normal named function:

function handleOrder(customerName: string) {
  console.log("Order received from:", customerName);
}

Then the parent passes that function to the child:

<OrderForm onSubmit={handleOrder} />

Here:

  • OrderForm is the child component.
  • onSubmit is our custom prop name.
  • handleOrder is the function value.

We write onSubmit={handleOrder} without parentheses because we are passing the function. We do not want to run it yet.

Later, the child runs it:

props.onSubmit(customerName);

A function passed through props and called later is often called a callback prop.


REACT · 04

4. Describe the Function Prop with TypeScript

TypeScript needs to know what the child expects to receive:

interface OrderFormProps {
  onSubmit: (customerName: string) => void;
}

Read the function type like this:

onSubmit must be a function
          ↓
it receives one string called customerName
          ↓
it does not need to return a value

The => above is part of a TypeScript type. It only describes the function prop. It does not create an arrow function.

Our real function still uses a normal declaration:

function handleOrder(customerName: string) {
  console.log(customerName);
}

This function matches the type because it receives one string and returns no useful value.


REACT · 05

5. Create the OrderForm Child Component

Project Structure

src/
├── components/
│   └── OrderForm.tsx
├── App.tsx
└── main.tsx

File: src/components/OrderForm.tsx

interface OrderFormProps {
  onSubmit: (customerName: string) => void;
}

export default function OrderForm(props: OrderFormProps) {
  function handleSubmit(formData: FormData) {
    const customerName = formData.get("customerName");

    if (typeof customerName !== "string") {
      return;
    }

    props.onSubmit(customerName);
  }

  return (
    <form action={handleSubmit}>
      <label htmlFor="customerName">Customer name</label>
      <input id="customerName" name="customerName" required />
      <button type="submit">Place order</button>
    </form>
  );
}

Browser Result

OrderForm displays the field and collects its value.

Follow the Important Lines

props: OrderFormProps means that the component receives the props described by the interface.

action={handleSubmit} tells React to run the form action after submission.

formData.get("customerName") reads the named input.

The typeof check confirms that the value is text.

props.onSubmit(customerName) sends that text to the function provided by the parent.


REACT · 06

6. Create the Parent App Component

File: src/App.tsx

import OrderForm from "./components/OrderForm";

export default function App() {
  function handleOrder(customerName: string) {
    console.log("Order received from:", customerName);
  }

  return (
    <main>
      <h1>Place your pizza order</h1>
      <OrderForm onSubmit={handleOrder} />
    </main>
  );
}

Browser Result

Place your pizza order

Console output

Order received from: Nikita

The child collects the name; the parent receives and processes it.

Follow the Connection

The parent creates handleOrder.

function handleOrder(customerName: string) {

The parent gives it to the child.

<OrderForm onSubmit={handleOrder} />

The child calls it after collecting the name.

props.onSubmit(customerName);

REACT · 07

7. Follow the Complete Data Flow

Imagine that the customer enters Nikita:

1. App creates handleOrder
             ↓
2. App passes handleOrder to OrderForm
             ↓
3. OrderForm receives it as props.onSubmit
             ↓
4. The customer enters "Nikita"
             ↓
5. The customer submits the form
             ↓
6. React calls handleSubmit(formData)
             ↓
7. OrderForm reads customerName
             ↓
8. OrderForm calls props.onSubmit("Nikita")
             ↓
9. App's handleOrder function runs
             ↓
10. Console: Order received from: Nikita

There are two directions:

Function: parent → child
Data:     child → parent, when the child calls the function

This pattern is often called lifting data up.


REACT · 08

8. Function Reference or Function Call?

These ideas are different.

Function Reference

handleOrder

This means the function itself. It does not run the function.

<OrderForm onSubmit={handleOrder} />

The parent passes a function reference to the child.

Function Call

handleOrder("Nikita");

The parentheses run the function.

Inside the child, this line calls the parent's function:

props.onSubmit(customerName);

Remember:

handleOrder             = pass or refer to the function
handleOrder("Nikita")   = run the function now

REACT · 09

9. Give Each Component One Job

The child form should collect the values. The parent should decide how to use them.

Part Job
OrderForm Display fields and collect values
App Process the submitted information
onSubmit prop Connect the child to the parent's function

OrderForm does not know whether the parent logs the name, saves an order, or sends an API request.

That is useful because the same form can work with different parent functions.


REACT · 10

10. Reuse the Same Form Component

The parent can display the same child more than once and give each copy a different function.

import OrderForm from "./components/OrderForm";

export default function App() {
  function handleDeliveryOrder(customerName: string) {
    console.log("Delivery order:", customerName);
  }

  function handlePickupOrder(customerName: string) {
    console.log("Pickup order:", customerName);
  }

  return (
    <main>
      <section>
        <h2>Delivery</h2>
        <OrderForm onSubmit={handleDeliveryOrder} />
      </section>

      <section>
        <h2>Pickup</h2>
        <OrderForm onSubmit={handlePickupOrder} />
      </section>
    </main>
  );
}

Browser Result

Delivery

Pickup

The same OrderForm component can call different parent functions.

The form UI is reused. Only the processing function changes.


REACT · 11

11. Build a Complete Pizza Choice Form

Now the child will collect both the customer name and the pizza name.

File: src/components/PizzaOrderForm.tsx

interface PizzaOrder {
  customerName: string;
  pizzaName: string;
}

interface PizzaOrderFormProps {
  onSubmit: (order: PizzaOrder) => void;
}

export default function PizzaOrderForm(props: PizzaOrderFormProps) {
  function handleSubmit(formData: FormData) {
    const customerName = formData.get("customerName");
    const pizzaName = formData.get("pizzaName");

    if (
      typeof customerName !== "string" ||
      typeof pizzaName !== "string"
    ) {
      return;
    }

    const order: PizzaOrder = {
      customerName: customerName,
      pizzaName: pizzaName,
    };

    props.onSubmit(order);
  }

  return (
    <form action={handleSubmit}>
      <label htmlFor="customerName">Customer name</label>
      <input id="customerName" name="customerName" required />

      <label htmlFor="pizzaName">Pizza</label>
      <input
        id="pizzaName"
        name="pizzaName"
        defaultValue="Margherita"
      />

      <button type="submit">Place order</button>
    </form>
  );
}

File: src/App.tsx

import PizzaOrderForm from "./components/PizzaOrderForm";

interface PizzaOrder {
  customerName: string;
  pizzaName: string;
}

export default function App() {
  function handleOrder(order: PizzaOrder) {
    console.log("Customer:", order.customerName);
    console.log("Pizza:", order.pizzaName);
  }

  return (
    <main>
      <h1>Pizza Palace</h1>
      <PizzaOrderForm onSubmit={handleOrder} />
    </main>
  );
}

Browser Result

Pizza Palace

Console output

Customer: Nikita

Pizza: Margherita

The child sends one order object; the parent reads its properties.

We write every object property explicitly. We do not destructure the order or the props.


REACT · 12

12. Avoid Common Beginner Mistakes

Mistake 1 — Giving the Prop the Wrong Type

Incorrect:

interface OrderFormProps {
  onSubmit: string;
}

Correct:

interface OrderFormProps {
  onSubmit: (customerName: string) => void;
}

The prop contains a function, not text.

Mistake 2 — Calling the Function Too Early

Incorrect:

<OrderForm onSubmit={handleOrder("Nikita")} />

Correct:

<OrderForm onSubmit={handleOrder} />

Pass the function without parentheses.

Mistake 3 — Forgetting props

Incorrect:

onSubmit(customerName);

Correct:

props.onSubmit(customerName);

Mistake 4 — Confusing Two Different onSubmit Names

This is a custom component prop:

<OrderForm onSubmit={handleOrder} />

This is a native form event prop:

<form onSubmit={handleSubmit}>

Our child uses a React Form Action:

<form action={handleSubmit}>

The same word may appear in different places, so always check which element or component owns it.


REACT · 13

13. Practice One Step at a Time

  1. Create OrderForm.tsx with one named input and a submit button.
  2. Add the OrderFormProps interface.
  3. Add a normal handleSubmit function declaration.
  4. Read customerName from FormData.
  5. Call props.onSubmit(customerName).
  6. Create a normal handleOrder function in App.tsx.
  7. Pass it with <OrderForm onSubmit={handleOrder} />.
  8. Add a pizza field after the first version works.

Build one step, test it, and then add the next step.


REACT · 14

14. Summary — Remember the Connection

Concept Simple Meaning
Parent component Displays the child and provides the processing function
Child component Displays the form and collects values
Props Information passed from parent to child
Callback prop A function passed to the child to be called later
props.onSubmit(value) The child calls the parent's function
Form Action Gives submitted FormData to the child handler

Final Code Pattern

src/components/OrderForm.tsx

interface OrderFormProps {
  onSubmit: (customerName: string) => void;
}

export default function OrderForm(props: OrderFormProps) {
  function handleSubmit(formData: FormData) {
    const customerName = formData.get("customerName");

    if (typeof customerName !== "string") {
      return;
    }

    props.onSubmit(customerName);
  }

  return (
    <form action={handleSubmit}>
      <label htmlFor="customerName">Customer name</label>
      <input id="customerName" name="customerName" required />
      <button type="submit">Place order</button>
    </form>
  );
}

src/App.tsx

import OrderForm from "./components/OrderForm";

export default function App() {
  function handleOrder(customerName: string) {
    console.log("Order received from:", customerName);
  }

  return (
    <main>
      <h1>Pizza Palace</h1>
      <OrderForm onSubmit={handleOrder} />
    </main>
  );
}

Browser Result

Pizza Palace

Console output

Order received from: Nikita

The parent passes a function; the child collects a value and calls it.
Parent passes function
        ↓
Child collects data
        ↓
Child calls function
        ↓
Parent processes data

Next lesson: Wait for the next source topic before adding more Module 3 material.

← Back↑ Back to the top