▶️ Live demo
Try it yourself — interact with the example below.
Loading demo…
🎯 What Is a Form in React?
Imagine you're ordering at a restaurant:
PAPER ORDER FORM (HTML Form):
┌─────────────────────────────────────────┐
│ 🍽️ RESTAURANT ORDER SLIP │
│ │
│ Quantity: [1 ▼] (dropdown) │
│ Item: [____________] (write here) │
│ │
│ [SUBMIT ORDER] (button) │
│ │
│ Problem: When you submit, waiter takes │
│ the paper to the kitchen. You wait. │
│ Page reloads = whole restaurant stops! │
│ │
│ In HTML: │
│ <form> │
│ <select>...</select> │
│ <input type="text"> │
│ <button>Submit</button> │
│ </form> │
│ // Click submit → PAGE RELOADS! │
└─────────────────────────────────────────┘
REACT FORM (Smart Form):
┌─────────────────────────────────────────┐
│ 📱 DIGITAL ORDER APP │
│ │
│ Quantity: [1 ▼] │
│ Item: [____________] │
│ │
│ [ADD TO ORDER] │
│ │
│ ✨ MAGIC: When you click submit: │
│ • Page does NOT reload! │
│ • Item appears instantly in your list! │
│ • You can keep ordering! │
│ • No waiting, no interruption! │
│ │
│ In React: │
│ <form onSubmit={handleSubmit}> │
│ <select>...</select> │
│ <input type="text"> │
│ <button>Add</button> │
│ </form> │
│ // e.preventDefault() stops reload! │
└─────────────────────────────────────────┘
THE ARRAY.FROM TRICK:
┌─────────────────────────────────────────┐
│ MANUAL: Writing 20 options by hand │
│ <option>1</option> │
│ <option>2</option> │
│ ... (boring! repetitive!) │
│ │
│ SMART: Generate automatically! │
│ Array.from({length: 20}, (_, i) => i+1)│
│ → [1, 2, 3, ..., 20] │
│ .map(num => <option>{num}</option>) │
│ → Automatic options! │
└─────────────────────────────────────────┘
⚠️ The Big Problem: "Form Submission Reloads the Page"
// ==========================================
// THE "PAGE RELOAD" TRAP
// ==========================================
// ❌ WRONG: Default form behavior
function BadForm() {
function handleSubmit() {
console.log("Form submitted!");
// But the page reloads! 😱
}
return (
<form onSubmit={handleSubmit}>
<input type="text" placeholder="Item name" />
<button>Add</button>
</form>
);
}
// What happens:
// 1. User types "Socks"
// 2. User clicks "Add" (or presses Enter)
// 3. Browser submits form to server
// 4. PAGE RELOADS! 💥
// 5. All React state is LOST!
// 6. User sees blank page for a moment
// 7. App restarts from scratch
// This is NOT a Single Page Application!
// This is old-school web behavior!
// ==========================================
// THE SOLUTION: Prevent Default + Event Handler
// ==========================================
// ✅ CORRECT: Stop the reload, handle in React
function GoodForm() {
function handleSubmit(event) {
// MAGIC LINE: Stop the browser from reloading!
event.preventDefault();
console.log("Form submitted!");
// Now we can do React stuff!
// Add to list, update state, etc.
// Page stays exactly the same! ✨
}
return (
<form onSubmit={handleSubmit}>
<select>
<option value="1">1</option>
<option value="2">2</option>
</select>
<input type="text" placeholder="Item name" />
<button>Add</button>
</form>
);
}
// What happens now:
// 1. User types "Socks"
// 2. User clicks "Add" (or presses Enter)
// 3. handleSubmit runs
// 4. event.preventDefault() stops the reload! 🛑
// 5. React stays in control!
// 6. Page does NOT refresh!
// 7. We can add "Socks" to our list instantly!
📋 Complete Visual Examples
Create file: react-forms-basics.js
// ==========================================
// FORMS IN REACT - Complete Guide
// ==========================================
/*
FORM ANATOMY:
┌─────────────────────────────────────────┐
│ <form onSubmit={handleSubmit}> │
│ ← Parent container, listens for submit│
│ │
│ <select> │
│ <option value="1">1</option> │
│ </select> │
│ ← Dropdown, user picks quantity │
│ │
│ <input type="text" /> │
│ ← Text field, user types description │
│ │
│ <button>Add</button> │
│ ← Clicking this triggers submit! │
│ │
│ </form> │
│ │
│ Submit happens when: │
│ • Clicking button inside form │
│ • Pressing Enter while in input field │
└─────────────────────────────────────────┘
ARRAY.FROM TRICK:
┌─────────────────────────────────────────┐
│ Array.from({length: 20}, (_, i) => i+1)│
│ │
│ {length: 20} ← Creates array-like │
│ object with 20 slots │
│ │
│ (_, i) => i+1 ← Map function: │
│ _ = current value (undefined, ignore) │
│ i = index (0, 1, 2, ...) │
│ i+1 = (1, 2, 3, ...) │
│ │
│ Result: [1, 2, 3, ..., 20] │
└─────────────────────────────────────────┘
*/
// ==========================================
// EXAMPLE 1: The Array.from Trick
// ==========================================
// MANUAL WAY (Boring, repetitive):
// <select>
// <option value="1">1</option>
// <option value="2">2</option>
// <option value="3">3</option>
// ... (imagine writing 20 of these!)
// </select>
// SMART WAY (Dynamic, automatic):
const numbers = Array.from({ length: 20 }, (_, i) => i + 1);
// Result: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
// How it works step by step:
// Array.from({ length: 20 }, ...)
// → "Create an array with 20 empty slots"
//
// (_, i) => i + 1
// → For each slot, return index + 1
// → i=0 → 1, i=1 → 2, i=2 → 3, ..., i=19 → 20
// Then map to JSX:
// numbers.map(num => <option value={num} key={num}>{num}</option>)
// ==========================================
// EXAMPLE 2: Basic Form Structure
// ==========================================
import { useState } from 'react';
function Form() {
// Handle form submission
function handleSubmit(event) {
// 🛑 STOP the browser from reloading!
event.preventDefault();
console.log("Form was submitted!");
// Later: Add item to list, clear inputs, etc.
}
return (
<form className="add-form" onSubmit={handleSubmit}>
{/* Quantity selector */}
<select>
{Array.from({ length: 20 }, (_, i) => i + 1).map(num => (
<option value={num} key={num}>
{num}
</option>
))}
</select>
{/* Item description */}
<input type="text" placeholder="Item..." />
{/* Submit button */}
<button>Add</button>
</form>
);
}
// Visual Result:
// ┌─────────────────────────────────────┐
// │ [ 1 ▼ ] [ Item... ] [Add] │
// └─────────────────────────────────────┘
//
// [ 1 ▼ ] = Select dropdown with 1-20
// [ Item... ] = Text input
// [Add] = Button that submits form
// ==========================================
// EXAMPLE 3: The Event Object
// ==========================================
function FormWithEvent() {
function handleSubmit(event) {
event.preventDefault();
// The event object contains EVERYTHING about the submission
console.log(event); // SyntheticBaseEvent { ... }
// event.target = the form element that was submitted
console.log(event.target); // <form>...</form>
// We can access form elements through event.target
// But in React, we usually use a BETTER way (controlled components)
}
return (
<form onSubmit={handleSubmit}>
<select>
<option value="1">1</option>
</select>
<input type="text" placeholder="Item..." />
<button>Add</button>
</form>
);
}
// What is event.preventDefault()?
//
// ┌─────────────────────────────────────────┐
// │ BROWSER DEFAULT BEHAVIOR: │
// │ Form submit → Send data to server │
// │ → Wait for response │
// │ → Reload page with response│
// │ → LOSE ALL REACT STATE! │
// │ │
// │ event.preventDefault(): │
// │ "Hey browser, DON'T do your default! │
// │ I got this. I'll handle it myself." │
// │ │
// │ Result: Page stays the same! │
// │ React stays in control! │
// │ No state lost! │
// └─────────────────────────────────────────┘
// ==========================================
// EXAMPLE 4: Why onSubmit on Form (Not onClick on Button)
// ==========================================
// ❌ WRONG: Only handles button clicks
function WrongForm() {
function handleClick() {
console.log("Button clicked!");
// Misses Enter key submissions!
}
return (
<form>
<input type="text" placeholder="Item..." />
<button onClick={handleClick}>Add</button>
</form>
);
}
// Problems:
// 1. Clicking button works
// 2. Pressing Enter in input → NOTHING HAPPENS! 😱
// 3. User expects Enter to work (standard web behavior)
// ✅ CORRECT: Handles BOTH button click AND Enter key
function CorrectForm() {
function handleSubmit(event) {
event.preventDefault();
console.log("Form submitted!");
// Works for:
// • Clicking the Add button
// • Pressing Enter while typing in input
}
return (
<form onSubmit={handleSubmit}>
<input type="text" placeholder="Item..." />
<button>Add</button>
</form>
);
}
// Why this works:
// HTML forms automatically submit on:
// 1. Clicking <button type="submit"> inside form
// 2. Pressing Enter while focused on any input inside form
//
// By putting onSubmit on the <form>, we catch BOTH!
// ==========================================
// EXAMPLE 5: Complete Form with All Elements
// ==========================================
function CompleteForm() {
function handleSubmit(event) {
event.preventDefault();
console.log("Adding item to list!");
}
return (
<form className="add-form" onSubmit={handleSubmit}>
{/* Quantity: How many of this item? */}
<select>
{Array.from({ length: 20 }, (_, i) => i + 1).map(num => (
<option value={num} key={num}>
{num}
</option>
))}
</select>
{/* Description: What is this item? */}
<input type="text" placeholder="Item..." />
{/* Submit: Add to packing list */}
<button>Add</button>
</form>
);
}
// Complete App with Form:
function App() {
return (
<div className="app">
<Logo />
<Form /> {/* ← Our new form! */}
<PackingList />
<Stats />
</div>
);
}
🚀 Interactive React Usage Examples
Complete React File: FormsBasicsMasterClass.jsx
import React, { useState } from 'react';
// ==========================================
// INTERACTIVE FORMS BASICS DEMO
// ==========================================
function FormsBasicsMasterClass() {
const [activeDemo, setActiveDemo] = useState('structure');
const demos = {
structure: { title: 'Form Structure', component: <FormStructureDemo /> },
array: { title: 'Array.from Trick', component: <ArrayFromDemo /> },
submit: { title: 'Handle Submit', component: <HandleSubmitDemo /> },
prevent: { title: 'preventDefault', component: <PreventDefaultDemo /> },
complete: { title: 'Complete Form', component: <CompleteFormDemo /> }
};
return (
<div style={{ maxWidth: '900px', margin: '0 auto', padding: '20px', fontFamily: 'Arial, sans-serif' }}>
<header style={{ background: '#e76f51', color: 'white', padding: '30px', borderRadius: '10px', marginBottom: '30px' }}>
<h1 style={{ margin: 0 }}>📝 Forms in React</h1>
<p style={{ margin: '10px 0 0 0', opacity: 0.9 }}>Building Interactive Forms Without Page Reloads</p>
</header>
<nav style={{ display: 'flex', gap: '10px', marginBottom: '30px', flexWrap: 'wrap' }}>
{Object.entries(demos).map(([key, { title }]) => (
<button
key={key}
onClick={() => setActiveDemo(key)}
style={{
padding: '12px 24px',
fontSize: '16px',
border: 'none',
borderRadius: '8px',
cursor: 'pointer',
backgroundColor: activeDemo === key ? '#264653' : '#e0e0e0',
color: activeDemo === key ? 'white' : '#333',
fontWeight: activeDemo === key ? 'bold' : 'normal'
}}
>
{title}
</button>
))}
</nav>
<main style={{ background: '#f8f9fa', padding: '30px', borderRadius: '10px', minHeight: '400px' }}>
{demos[activeDemo].component}
</main>
</div>
);
}
// ==========================================
// DEMO 1: Form Structure
// ==========================================
function FormStructureDemo() {
const [highlighted, setHighlighted] = useState(null);
const elements = [
{ id: 'form', name: '<form>', desc: 'Container that listens for submit', color: '#e76f51' },
{ id: 'select', name: '<select>', desc: 'Dropdown for quantity (1-20)', color: '#2a9d8f' },
{ id: 'input', name: '<input>', desc: 'Text field for item name', color: '#e9c46a' },
{ id: 'button', name: '<button>', desc: 'Triggers form submission', color: '#264653' }
];
return (
<div>
<h2>Form Structure 🏗️</h2>
<div style={{ marginBottom: '20px', padding: '15px', background: '#e3f2fd', borderRadius: '8px' }}>
<h4>A form has 3 main parts:</h4>
</div>
<div style={{
padding: '30px',
background: 'white',
borderRadius: '10px',
boxShadow: '0 2px 10px rgba(0,0,0,0.1)'
}}>
<form style={{
display: 'flex',
gap: '10px',
alignItems: 'center',
padding: '20px',
background: '#f5f5f5',
borderRadius: '8px',
border: highlighted ? `3px solid ${elements.find(e => e.id === highlighted)?.color}` : '2px solid #e0e0e0'
}}>
<div
onClick={() => setHighlighted('select')}
style={{
flex: '0 0 80px',
padding: '10px',
background: highlighted === 'select' ? '#2a9d8f' : 'white',
color: highlighted === 'select' ? 'white' : '#333',
borderRadius: '5px',
cursor: 'pointer',
border: '2px solid #2a9d8f'
}}
>
<div style={{ fontSize: '12px', opacity: 0.7 }}><select></div>
<div style={{ fontWeight: 'bold' }}>[ 1 ▼ ]</div>
</div>
<div
onClick={() => setHighlighted('input')}
style={{
flex: 1,
padding: '10px',
background: highlighted === 'input' ? '#e9c46a' : 'white',
color: highlighted === 'input' ? '#264653' : '#333',
borderRadius: '5px',
cursor: 'pointer',
border: '2px solid #e9c46a'
}}
>
<div style={{ fontSize: '12px', opacity: 0.7 }}><input type="text"></div>
<div style={{ color: '#999' }}>Item...</div>
</div>
<div
onClick={() => setHighlighted('button')}
style={{
padding: '10px 20px',
background: highlighted === 'button' ? '#264653' : '#7950f2',
color: 'white',
borderRadius: '5px',
cursor: 'pointer',
border: '2px solid #264653'
}}
>
<div style={{ fontSize: '12px', opacity: 0.7 }}><button></div>
<div style={{ fontWeight: 'bold' }}>Add</div>
</div>
</form>
<div
onClick={() => setHighlighted('form')}
style={{
marginTop: '10px',
padding: '10px',
textAlign: 'center',
background: highlighted === 'form' ? '#e76f51' : '#f0f0f0',
color: highlighted === 'form' ? 'white' : '#666',
borderRadius: '5px',
cursor: 'pointer'
}}
>
<div style={{ fontSize: '12px' }}>{'<form onSubmit={handleSubmit}>'}</div>
<div>Parent container - catches ALL submit events!</div>
</div>
</div>
{highlighted && (
<div style={{ marginTop: '20px', padding: '20px', background: '#fff3e0', borderRadius: '8px' }}>
<h4 style={{ color: elements.find(e => e.id === highlighted)?.color }}>
{elements.find(e => e.id === highlighted)?.name}
</h4>
<p>{elements.find(e => e.id === highlighted)?.desc}</p>
</div>
)}
</div>
);
}
// ==========================================
// DEMO 2: Array.from Trick
// ==========================================
function ArrayFromDemo() {
const [count, setCount] = useState(5);
const numbers = Array.from({ length: count }, (_, i) => i + 1);
return (
<div>
<h2>The Array.from Trick 🔢</h2>
<div style={{ marginBottom: '20px', padding: '15px', background: '#e3f2fd', borderRadius: '8px' }}>
<h4>Generate numbers 1 to N automatically!</h4>
</div>
<div style={{ marginBottom: '20px' }}>
<label style={{ marginRight: '10px' }}>How many options?</label>
<input
type="range"
min="1"
max="20"
value={count}
onChange={e => setCount(Number(e.target.value))}
style={{ width: '200px' }}
/>
<span style={{ marginLeft: '10px', fontWeight: 'bold', color: '#7950f2' }}>{count}</span>
</div>
<div style={{ display: 'grid', gap: '20px', gridTemplateColumns: '1fr 1fr' }}>
<div style={{ padding: '20px', background: 'white', borderRadius: '8px', border: '2px solid #264653' }}>
<h3 style={{ color: '#264653', marginTop: 0 }}>The Code:</h3>
<pre style={{
background: '#1e1e1e',
color: '#d4d4d4',
padding: '15px',
borderRadius: '4px',
fontSize: '14px'
}}>
{`Array.from(
{ length: ${count} },
(_, i) => i + 1
)`}
</pre>
<div style={{ marginTop: '15px', padding: '10px', background: '#e8f5e9', borderRadius: '4px', fontSize: '14px' }}>
<strong>Result:</strong> [{numbers.join(', ')}]
</div>
</div>
<div style={{ padding: '20px', background: 'white', borderRadius: '8px', border: '2px solid #2a9d8f' }}>
<h3 style={{ color: '#2a9d8f', marginTop: 0 }}>How It Works:</h3>
<div style={{ fontSize: '15px', lineHeight: '2' }}>
<p><code>{`{ length: ${count} }`}</code> → Array-like object with {count} slots</p>
<p><code>(_, i) => i + 1</code> → For each slot, return index + 1</p>
<p><code>_</code> = current value (ignore it)</p>
<p><code>i</code> = index (0, 1, 2, ...)</p>
<p><code>i + 1</code> = (1, 2, 3, ...)</p>
</div>
</div>
</div>
<div style={{ marginTop: '20px', padding: '15px', background: '#fff3e0', borderRadius: '8px' }}>
<h4>🧠 Memory Trick:</h4>
<p><code>Array.from({`{length: N}`}, (_, i) => i + 1)</code> = "Create N numbers starting from 1"</p>
</div>
</div>
);
}
// ==========================================
// DEMO 3: Handle Submit
// ==========================================
function HandleSubmitDemo() {
const [submitCount, setSubmitCount] = useState(0);
const [lastMethod, setLastMethod] = useState(null);
function handleSubmit(event) {
event.preventDefault();
setSubmitCount(c => c + 1);
}
function handleButtonClick() {
setLastMethod('button click');
// This ONLY catches button clicks, NOT Enter key!
}
return (
<div>
<h2>Handle Submit vs onClick 🖱️</h2>
<div style={{ marginBottom: '20px', padding: '15px', background: '#e3f2fd', borderRadius: '8px' }}>
<h4>onSubmit on form catches BOTH button click AND Enter key!</h4>
</div>
<div style={{ display: 'grid', gap: '20px', gridTemplateColumns: '1fr 1fr' }}>
{/* WRONG WAY */}
<div style={{ padding: '20px', background: '#ffebee', borderRadius: '8px', border: '2px solid #ef5350' }}>
<h3 style={{ color: '#c62828', marginTop: 0 }}>❌ onClick on Button</h3>
<form style={{ display: 'flex', gap: '10px' }}>
<input type="text" placeholder="Type here..." style={{ flex: 1, padding: '8px' }} />
<button
type="button"
onClick={handleButtonClick}
style={{ padding: '8px 16px', background: '#ef5350', color: 'white', border: 'none', borderRadius: '5px' }}
>
Add (onClick)
</button>
</form>
<p style={{ color: '#666', fontSize: '14px', marginTop: '10px' }}>
Try pressing Enter while typing... Nothing happens! 😱
</p>
</div>
{/* CORRECT WAY */}
<div style={{ padding: '20px', background: '#e8f5e9', borderRadius: '8px', border: '2px solid #4caf50' }}>
<h3 style={{ color: '#2e7d32', marginTop: 0 }}>✅ onSubmit on Form</h3>
<form
onSubmit={handleSubmit}
style={{ display: 'flex', gap: '10px' }}
>
<input type="text" placeholder="Type here..." style={{ flex: 1, padding: '8px' }} />
<button
style={{ padding: '8px 16px', background: '#4caf50', color: 'white', border: 'none', borderRadius: '5px' }}
>
Add (onSubmit)
</button>
</form>
<p style={{ color: '#666', fontSize: '14px', marginTop: '10px' }}>
Press Enter OR click button - BOTH work! ✨
</p>
</div>
</div>
<div style={{ marginTop: '20px', padding: '20px', background: 'white', borderRadius: '8px', textAlign: 'center' }}>
<div style={{ fontSize: '48px', fontWeight: 'bold', color: '#7950f2' }}>
{submitCount}
</div>
<p style={{ color: '#666' }}>Form submissions caught</p>
</div>
<div style={{ marginTop: '20px', padding: '15px', background: '#fff3e0', borderRadius: '8px' }}>
<h4>🧠 Why onSubmit is Better:</h4>
<ul>
<li>Catches button clicks</li>
<li>Catches Enter key presses</li>
<li>Standard HTML behavior (users expect it!)</li>
<li>One handler for multiple triggers</li>
</ul>
</div>
</div>
);
}
// ==========================================
// DEMO 4: preventDefault
// ==========================================
function PreventDefaultDemo() {
const [submissions, setSubmissions] = useState([]);
const [usePrevent, setUsePrevent] = useState(true);
function handleSubmit(event) {
if (usePrevent) {
event.preventDefault(); // 🛑 STOP the reload!
}
// If we don't prevent, page reloads and we lose everything!
setSubmissions(prev => [...prev, `Submission #${prev.length + 1} at ${new Date().toLocaleTimeString()}`]);
}
return (
<div>
<h2>event.preventDefault() 🛑</h2>
<div style={{ marginBottom: '20px', padding: '15px', background: '#e3f2fd', borderRadius: '8px' }}>
<h4>This single line stops the page from reloading!</h4>
</div>
<div style={{ marginBottom: '20px' }}>
<label style={{ display: 'flex', alignItems: 'center', gap: '10px', cursor: 'pointer' }}>
<input
type="checkbox"
checked={usePrevent}
onChange={() => setUsePrevent(!usePrevent)}
/>
<span>Use event.preventDefault()</span>
</label>
</div>
<div style={{
padding: '20px',
background: usePrevent ? '#e8f5e9' : '#ffebee',
borderRadius: '8px',
border: `2px solid ${usePrevent ? '#4caf50' : '#ef5350'}`
}}>
<form onSubmit={handleSubmit} style={{ display: 'flex', gap: '10px', marginBottom: '15px' }}>
<input type="text" placeholder="Type something..." style={{ flex: 1, padding: '8px' }} />
<button style={{ padding: '8px 16px', background: usePrevent ? '#4caf50' : '#ef5350', color: 'white', border: 'none', borderRadius: '5px' }}>
Submit
</button>
</form>
<div style={{ fontSize: '14px', color: '#666' }}>
{usePrevent ? (
<p>✅ Page will NOT reload. React stays in control!</p>
) : (
<p>❌ Page WILL reload. You'll lose this list! (Try it!)</p>
)}
</div>
</div>
<div style={{ marginTop: '20px' }}>
<h4>Submission History:</h4>
{submissions.length === 0 ? (
<p style={{ color: '#999' }}>No submissions yet...</p>
) : (
<ul style={{ listStyle: 'none', padding: 0 }}>
{submissions.map((sub, i) => (
<li key={i} style={{ padding: '8px', background: 'white', marginBottom: '5px', borderRadius: '4px' }}>
{sub}
</li>
))}
</ul>
)}
</div>
<div style={{ marginTop: '20px', padding: '15px', background: '#fff3e0', borderRadius: '8px' }}>
<h4>🧠 What preventDefault Does:</h4>
<div style={{ fontFamily: 'monospace', fontSize: '14px', lineHeight: '2' }}>
Browser: "I'm gonna reload the page now!"<br/>
React: "event.preventDefault()!"<br/>
Browser: "Oh, OK, never mind..."<br/>
Page: "I stay exactly the same!" ✨
</div>
</div>
</div>
);
}
// ==========================================
// DEMO 5: Complete Form
// ==========================================
function CompleteFormDemo() {
const [items, setItems] = useState([
{ id: 1, description: "Passport", quantity: 1, packed: false },
{ id: 2, description: "Socks", quantity: 6, packed: false }
]);
function handleSubmit(event) {
event.preventDefault();
// Get values from form (we'll learn controlled components next!)
const form = event.target;
const quantity = form.quantity.value;
const description = form.description.value;
if (!description) return; // Don't add empty items
const newItem = {
id: Date.now(),
description,
quantity: Number(quantity),
packed: false
};
setItems(prev => [...prev, newItem]);
form.reset(); // Clear the form
}
return (
<div>
<h2>Complete Form 🎉</h2>
<div style={{ marginBottom: '20px', padding: '15px', background: '#e3f2fd', borderRadius: '8px' }}>
<h4>Everything put together!</h4>
</div>
<div style={{
maxWidth: '500px',
margin: '0 auto',
background: 'white',
borderRadius: '10px',
overflow: 'hidden',
boxShadow: '0 4px 20px rgba(0,0,0,0.2)'
}}>
{/* Form */}
<form
onSubmit={handleSubmit}
style={{
display: 'flex',
gap: '10px',
padding: '20px',
background: '#e76f51',
alignItems: 'center'
}}
>
<select
name="quantity"
style={{
padding: '10px',
borderRadius: '5px',
border: 'none',
fontSize: '16px'
}}
>
{Array.from({ length: 20 }, (_, i) => i + 1).map(num => (
<option value={num} key={num}>{num}</option>
))}
</select>
<input
type="text"
name="description"
placeholder="Item..."
style={{
flex: 1,
padding: '10px',
borderRadius: '5px',
border: 'none',
fontSize: '16px'
}}
/>
<button
style={{
padding: '10px 20px',
background: '#264653',
color: 'white',
border: 'none',
borderRadius: '5px',
fontSize: '16px',
cursor: 'pointer'
}}
>
Add
</button>
</form>
{/* List */}
<div style={{ padding: '20px', background: '#f4a261', minHeight: '150px' }}>
{items.length === 0 ? (
<p style={{ textAlign: 'center', color: 'white' }}>No items yet. Add some!</p>
) : (
<ul style={{ listStyle: 'none', padding: 0, margin: 0 }}>
{items.map(item => (
<li key={item.id} style={{
padding: '10px',
marginBottom: '5px',
background: 'white',
borderRadius: '5px',
display: 'flex',
gap: '10px'
}}>
<span style={{ background: '#7950f2', color: 'white', padding: '2px 8px', borderRadius: '50%', fontSize: '14px' }}>
{item.quantity}
</span>
<span>{item.description}</span>
</li>
))}
</ul>
)}
</div>
{/* Stats */}
<div style={{ padding: '15px', background: '#2a9d8f', color: 'white', textAlign: 'center' }}>
<em>You have {items.length} items on your list</em>
</div>
</div>
<div style={{ marginTop: '20px', padding: '15px', background: '#e8f5e9', borderRadius: '8px' }}>
<h4>✅ What This Form Does:</h4>
<ol>
<li>User selects quantity (1-20) from dropdown</li>
<li>User types item description</li>
<li>User clicks Add (or presses Enter)</li>
<li><code>handleSubmit</code> runs</li>
<li><code>event.preventDefault()</code> stops reload</li>
<li>New item added to list instantly!</li>
<li>Form clears, ready for next item</li>
</ol>
</div>
</div>
);
}
export default FormsBasicsMasterClass;
🧠 Memory Aids for Poor Logic Thinking
The "Restaurant Order" Analogy
┌─────────────────────────────────────────────────┐
│ HTML FORM = PAPER ORDER SLIP │
│ REACT FORM = DIGITAL ORDER APP │
│ │
│ PAPER ORDER (HTML): │
│ ┌─────────────────────────────────────────┐ │
│ │ Waiter takes paper to kitchen │ │
│ │ You WAIT for food │ │
│ │ Kitchen sends food back │ │
│ │ PAGE RELOADS (whole restaurant stops) │ │
│ │ You get new paper │ │
│ │ Start over! │ │
│ └─────────────────────────────────────────┘ │
│ │
│ DIGITAL ORDER (React): │
│ ┌─────────────────────────────────────────┐ │
│ │ Tap "Order" on app │ │
│ │ Order goes to kitchen INSTANTLY │ │
│ │ You stay on same screen! │ │
│ │ NO RELOAD! │ │
│ │ See order added to your list! │ │
│ │ Keep ordering more! │ │
│ └─────────────────────────────────────────┘ │
│ │
│ event.preventDefault() = "Don't take the │
│ paper to the kitchen! I'll handle it myself!" │
└─────────────────────────────────────────────────┘
The "Array.from" Analogy
┌─────────────────────────────────────────────────┐
│ ARRAY.FROM = CONVEYOR BELT FACTORY │
│ │
│ You want 20 numbered boxes: │
│ │
│ MANUAL WAY: │
│ ┌─────────────────────────────────────────┐ │
│ │ Worker 1 writes "1" on box │ │
│ │ Worker 2 writes "2" on box │ │
│ │ Worker 3 writes "3" on box │ │
│ │ ... │ │
│ │ Worker 20 writes "20" on box │ │
│ │ │ │
│ │ Boring! Slow! Error-prone! │ │
│ └─────────────────────────────────────────┘ │
│ │
│ ARRAY.FROM WAY: │
│ ┌─────────────────────────────────────────┐ │
│ │ 🏭 CONVEYOR BELT │ │
│ │ │ │
│ │ {length: 20} = "Make belt with 20 │ │
│ │ empty slots" │ │
│ │ │ │
│ │ (_, i) => i + 1 = "For each slot, │ │
│ │ ignore what's there (_), │ │
│ │ use slot number (i), │ │
│ │ add 1 to get 1, 2, 3..." │ │
│ │ │ │
│ │ Result: [1, 2, 3, ..., 20] │ │
│ │ Automatic! Fast! No errors! │ │
│ └─────────────────────────────────────────┘ │
│ │
│ MEMORY TRICK: │
│ Array.from({length: N}, (_, i) => i + 1) │
│ = "Create N things numbered 1 to N" │
│ │
│ The _ means "I don't care about this value" │
│ The i means "I want the index/position" │
└─────────────────────────────────────────────────┘
The "Form Submit" Traffic Light Analogy
┌─────────────────────────────────────────────────┐
│ FORM SUBMIT = TRAFFIC INTERSECTION │
│ │
│ WITHOUT preventDefault(): │
│ ┌─────────────────────────────────────────┐ │
│ │ 🚦 GREEN LIGHT (Browser Default) │ │
│ │ │ │
│ │ Cars (data) go to server │ │
│ │ Wait for server response │ │
│ │ Page reloads (traffic stops!) │ │
│ │ Everything resets (React state lost) │ │
│ │ │ │
│ │ User: "Where did my list go?!" │ │
│ └─────────────────────────────────────────┘ │
│ │
│ WITH preventDefault(): │
│ ┌─────────────────────────────────────────┐ │
│ │ 🚦 RED LIGHT (React Says STOP!) │ │
│ │ │ │
│ │ Cars (data) stay on page │ │
│ │ React handles everything locally │ │
│ │ No reload (traffic keeps flowing!) │ │
│ │ State preserved (list stays!) │ │
│ │ │ │
│ │ User: "My list is still here!" │ │
│ └─────────────────────────────────────────┘ │
│ │
│ event.preventDefault() = "Red light! │
│ Don't send to server! I'll handle it!" │
└─────────────────────────────────────────────────┘
The "onSubmit vs onClick" Analogy
┌─────────────────────────────────────────────────┐
│ onSubmit vs onClick = DOORBELL vs KNOCKING │
│ │
│ onClick on button = KNOCKING ON ONE DOOR │
│ ┌─────────────────────────────────────────┐ │
│ │ You knock on the front door │ │
│ │ Someone answers │ │
│ │ │ │
│ │ But what if they come through the │ │
│ │ back door? You miss them! │ │
│ │ │ │
│ │ onClick only catches BUTTON CLICKS │ │
│ │ Misses Enter key! │ │
│ └─────────────────────────────────────────┘ │
│ │
│ onSubmit on form = SECURITY SYSTEM │
│ ┌─────────────────────────────────────────┐ │
│ │ Motion sensors on ALL doors │ │
│ │ Front door → Caught! │ │
│ │ Back door → Caught! │ │
│ │ Window → Caught! │ │
│ │ │ │
│ │ onSubmit catches: │ │
│ │ • Button click │ │
│ │ • Enter key press │ │
│ │ • Any form submission method! │ │
│ └─────────────────────────────────────────┘ │
│ │
│ ALWAYS use onSubmit on the form! │
│ It's like having security on ALL entrances! │
└─────────────────────────────────────────────────┘
🎓 Practice Exercises
Exercise 1: Create a Select with Options
Create a dropdown with numbers 1-10 using Array.from:
function NumberSelect() {
// TODO: Use Array.from to create options 1-10
return (
<select>
{/* Your code here */}
</select>
);
}
Solution:
function NumberSelect() {
return (
<select>
{Array.from({ length: 10 }, (_, i) => i + 1).map(num => (
<option value={num} key={num}>
{num}
</option>
))}
</select>
);
}
Exercise 2: Basic Form with Submit Handler
Create a form that logs to console on submit without reloading:
function SimpleForm() {
// TODO: Add handleSubmit function
// TODO: Prevent default behavior
// TODO: Log "Submitted!" to console
return (
<form>
<input type="text" placeholder="Your name" />
<button>Submit</button>
</form>
);
}
Solution:
function SimpleForm() {
function handleSubmit(event) {
event.preventDefault(); // 🛑 Stop reload!
console.log("Submitted!");
}
return (
<form onSubmit={handleSubmit}>
<input type="text" placeholder="Your name" />
<button>Submit</button>
</form>
);
}
Exercise 3: Complete Packing Form
Build the complete form with select, input, and button:
function PackingForm() {
// TODO: Create handleSubmit with preventDefault
// TODO: Add select with 1-20 options
// TODO: Add text input for item
// TODO: Add submit button
return (
<form className="add-form">
{/* Your code here */}
</form>
);
}
Solution:
function PackingForm() {
function handleSubmit(event) {
event.preventDefault();
console.log("Adding item to packing list!");
}
return (
<form className="add-form" onSubmit={handleSubmit}>
<select>
{Array.from({ length: 20 }, (_, i) => i + 1).map(num => (
<option value={num} key={num}>
{num}
</option>
))}
</select>
<input type="text" placeholder="Item..." />
<button>Add</button>
</form>
);
}
Exercise 4: Form with Multiple Submit Methods
Prove that onSubmit catches both button click AND Enter key:
function MultiSubmitForm() {
const [submissions, setSubmissions] = useState([]);
// TODO: Create handleSubmit
// TODO: Add submission to list with timestamp
// TODO: Show list of submissions
return (
<div>
<form>
<input type="text" placeholder="Type and press Enter or click button" />
<button>Submit</button>
</form>
{/* Show submissions here */}
</div>
);
}
Solution:
import { useState } from 'react';
function MultiSubmitForm() {
const [submissions, setSubmissions] = useState([]);
function handleSubmit(event) {
event.preventDefault();
const input = event.target.querySelector('input');
const value = input.value;
if (!value) return;
setSubmissions(prev => [
...prev,
`"${value}" submitted at ${new Date().toLocaleTimeString()}`
]);
input.value = ''; // Clear input
}
return (
<div style={{ padding: '20px' }}>
<form onSubmit={handleSubmit} style={{ display: 'flex', gap: '10px', marginBottom: '20px' }}>
<input
type="text"
placeholder="Type and press Enter or click button"
style={{ flex: 1, padding: '10px' }}
/>
<button style={{ padding: '10px 20px' }}>Submit</button>
</form>
<div>
<h4>Submissions:</h4>
{submissions.length === 0 ? (
<p style={{ color: '#999' }}>Try pressing Enter OR clicking the button!</p>
) : (
<ul>
{submissions.map((sub, i) => (
<li key={i} style={{ padding: '5px', background: '#f5f5f5', marginBottom: '5px' }}>
{sub}
</li>
))}
</ul>
)}
</div>
</div>
);
}
💡 Key Takeaways
| Concept | What It Means | Example |
|---|---|---|
<form> | Container that catches submit events | <form onSubmit={handleSubmit}> |
<select> | Dropdown menu | <select><option>1</option></select> |
<input type="text"> | Text input field | <input placeholder="Item..." /> |
<button> | Submit button (inside form) | <button>Add</button> |
onSubmit | Event handler on form | Catches button click AND Enter key |
event.preventDefault() | Stops browser reload | event.preventDefault() |
Array.from({length: N}) | Creates array with N slots | Array.from({length: 20}) |
(_, i) => i + 1 | Map function for numbers | Returns index + 1 for each slot |
.map() on Array.from | Transforms numbers to JSX | .map(num => <option>{num}</option>) |
key prop | Unique identifier for list items | key={num} |
The Complete Form Pattern:
function Form() {
function handleSubmit(event) {
event.preventDefault(); // 🛑 STOP reload!
// Handle the form data here
console.log("Form submitted!");
}
return (
<form onSubmit={handleSubmit}>
<select>
{Array.from({ length: 20 }, (_, i) => i + 1).map(num => (
<option value={num} key={num}>{num}</option>
))}
</select>
<input type="text" placeholder="Item..." />
<button>Add</button>
</form>
);
}
Golden Rules:
- Always use
onSubmiton<form>— NotonClickon button - Always call
event.preventDefault()— Stops page reload - Use
Array.fromfor number ranges — Don't write options manually - Always add
keyprop when mapping — Use unique values - Form submits on button click AND Enter key — Both trigger onSubmit
- Keep form elements inside
<form>— That's how HTML knows they're connected - Handle data in the submit handler — Not in individual change handlers (for now)
One Sentence Summary: > "To build a form in React, you wrap a select dropdown (populated dynamically with Array.from and map), a text input, and a submit button inside a form element with an onSubmit handler that receives the event object and immediately calls event.preventDefault() to stop the browser from reloading the page, allowing React to handle the submission in a single page application where both clicking the button and pressing Enter inside the input trigger the same submit event!"