← React / TypeScript

React / TypeScript · 11 / 18

Forms, lists and effects

Validate forms and deliberately synchronise with an external browser function.

  • Medium
  • Intermediate
  • 45 min

Prerequisites: First React app: JSX, props and state

A form initially holds a draft. Confirmation turns it into a new entry. The state owner manages this change. An effect synchronises with something outside React’s calculation, here the browser window title. These tasks should not be mixed. Input is processed in an action, while the window title is handled by an effect.

Example files

01Extend the interface

Use the project from tutorial 10 and replace src/App.tsx. value and onChange make the field controlled: React owns the displayed text. label and htmlFor connect the field to its label.

src/App.tsx · tsxOpen file
import React, { useEffect, useState } from 'react';
export function App() {
  const [title, setTitle] = useState('');
  const [entries, setEntries] = useState<string[]>([]);
  const [error, setError] = useState('');
  useEffect(() => {
    const previous = document.title;
    document.title = `Entries: ${entries.length}`;
    return () => { document.title = previous; };
  }, [entries.length]);
  function submit(event: React.FormEvent) {
    event.preventDefault();
    const value = title.trim();
    if (!value) { setError('Title required'); return; }
    setEntries(old => [...old, value]);
    setTitle(''); setError('');
  }
  return <main><h1>House notes</h1><form onSubmit={submit}>
    <label htmlFor="title">Title</label>
    <input id="title" value={title} onChange={event => setTitle(event.target.value)} />
    <button>Add</button><p role="alert">{error}</p>
  </form><ul>{entries.map((value, index) => <li key={index}>{value}</li>)}</ul></main>;
}

02Use submit rather than rendering

preventDefault stops normal HTML form navigation. The action normalises title and rejects empty text. The functional state update creates a new list and preserves existing entries.

03Clean up an effect

The entries.length dependency says when synchronisation is needed again. The returned function restores the previous window title. An effect needs appropriate cleanup when it changes external resources or subscriptions.

04Distinguish drafts from data

After successful submission, we clear only title and error. entries remains. This is still not a database commit. FlowForm Studio explicitly distinguishes UI drafts, server state and version conflicts.

Expected result

Whitespace produces Title required. Roof creates a list item and sets the window title to Entries: 1.

Your exercise

Add permanently unique IDs to list entries. Why is the index insufficient for later deletion or reordering?

Show the solution and explanation

Use objects with id and title. Generate the ID once in submit using crypto.randomUUID and use key={entry.id}. An index is tolerable only in this append-only learning example. Reordering would confuse identity with position.

Project context and sources

The example is simplified for learning. The linked source version shows its use in the project and the checks applied there.