Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Forms and controlled inputs

In React the value of an input field is usually kept in state. Such a field is called controlled: what is in state is what the field shows, and every keystroke updates the state. The text is always at hand — you can validate it, clear it or send it.

A controlled field

Create src/components/AddTodoForm.jsx:

// src/components/AddTodoForm.jsx
import { useState } from 'react';
 
export default function AddTodoForm() {
  const [text, setText] = useState('');
 
  return (
    <>
      <input value={text} onChange={(event) => setText(event.target.value)} />
      <p>You typed: {text}</p>
    </>
  );
}

Render <AddTodoForm /> in App. The text under the field changes as you type: value comes from state and onChange updates it.

Submitting the form

onSubmit goes on the form, not on the button — then the Enter key works too. preventDefault() cancels the default page reload:

// src/components/AddTodoForm.jsx
import { useState } from 'react';
 
export default function AddTodoForm({ onAdd }) {
  const [text, setText] = useState('');
 
  function handleSubmit(event) {
    event.preventDefault();
    onAdd(text);
    setText('');
  }
 
  return (
    <form onSubmit={handleSubmit}>
      <input value={text} onChange={(event) => setText(event.target.value)} />
      <button type="submit">Add</button>
    </form>
  );
}

The form passes the text to its parent through onAdd and clears the field.

Connecting it to the list

In src/App.jsx a task is created from the form text:

// src/App.jsx
function handleAdd(text) {
  setTodos([...todos, { id: crypto.randomUUID(), text, done: false }]);
}
 
// in the markup:
<AddTodoForm onAdd={handleAdd} />
<TodoList todos={todos} onToggle={handleToggle} onDelete={handleDelete} />

Type "Water the plants" and press Enter — the task appears at the end of the list and the field empties.

Validating input

The error is computed from state; it needs no state of its own:

// src/components/AddTodoForm.jsx
const trimmed = text.trim();
 
// in the markup:
<button type="submit" disabled={trimmed === ''}>Add</button>
{text.length > 100 && <p>No more than 100 characters</p>}

While the field is empty or holds only spaces, the button is disabled.

Other field types

FieldValue attributeWhat to read in onChange
input, textareavalueevent.target.value
selectvalueevent.target.value
input type="checkbox"checkedevent.target.checked

In React textarea and select also have a value attribute — there is no need to put text inside the tag or selected on an option.

Common mistake

A field with value but no onChange becomes read-only — you cannot type anything, and the console shows a warning:

// Wrong: the text in the field does not change.
<input value={text} />
 
// Correct:
<input value={text} onChange={(event) => setText(event.target.value)} />

If a field does not need to be controlled, use defaultValue instead of value.

Thanks for reading!

Donate to the site — we work hard for you.