DevNotes

DevNotes

@devnotes ·

React Part 5: Events and forms

Clicks, typing and form submits work a little differently in React than in plain JavaScript, and nearly every React form is built on one pattern: controlled inputs.

Handling events

JSX
function SaveButton() {  function handleClick() {    console.log("Saved");  }
  return <button onClick={handleClick}>Save</button>;}

Events are props, named in camelCase: onClick, onChange, onSubmit, onKeyDown. Pass the function itself: onClick={handleClick}. Written as onClick={handleClick()}, it runs every time the component draws, not on the click.

To pass a value along, wrap the call in an arrow function: onClick={() => removeItem(item.id)}.

Controlled inputs

In React, an input usually gets its value from state, and updates that state on every key press:

JSX
import { useState } from "react";
function NameField() {  const [name, setName] = useState("");
  return (    <>      <input value={name} onChange={(e) => setName(e.target.value)} />      <p>Hello, {name || "stranger"}</p>    </>  );}

value={name} means the box always shows what's in state. onChange runs on every key press (in React it acts like the input event from JavaScript Part 9), and e.target.value is the new text. Type a letter, the state updates, the component draws again, and the box shows the letter.

It sounds roundabout, but it means the value lives in your state, ready for checking, counting or sending, without ever reading it out of the input.

A complete form

JSX
function Signup() {  const [email, setEmail] = useState("");  const [agree, setAgree] = useState(false);  const [message, setMessage] = useState("");
  function handleSubmit(e) {    e.preventDefault();    if (!email.includes("@")) {      setMessage("Please enter a valid email.");      return;    }    setMessage(`Thanks! We'll write to ${email}.`);  }
  return (    <form onSubmit={handleSubmit} noValidate>      <label htmlFor="email">Email</label>      <input        id="email"        type="email"        value={email}        onChange={(e) => setEmail(e.target.value)}      />      <label>        <input          type="checkbox"          checked={agree}          onChange={(e) => setAgree(e.target.checked)}        />        I accept the club rules      </label>      <button type="submit" disabled={!agree}>Sign up</button>      {message && <p>{message}</p>}    </form>  );}

What to notice:

  • onSubmit goes on the form, and e.preventDefault() stops the page reload, just as in plain JavaScript.
  • noValidate turns off the browser's own checks, like the email one, so your code does the checking. Without it, the browser blocks a bad address before your code runs, and your message never shows.
  • Checkboxes use checked and e.target.checked instead of value.
  • The button stays disabled until the box is ticked. That's state driving the page, with no DOM code at all.

Many fields, one state object

With lots of fields, keep them in one object and give each input a name:

JSX
const [form, setForm] = useState({ name: "", city: "" });
function handleChange(e) {  setForm({ ...form, [e.target.name]: e.target.value });}
<input name="name" value={form.name} onChange={handleChange} /><input name="city" value={form.city} onChange={handleChange} />

[e.target.name] uses the input's name as the key, so one function updates whichever field changed.

Try it

Build a feedback form with a name, a rating from 1 to 5 in a <select>, and a comment box that shows how many characters are left out of 300. Once it's submitted, show a thank-you message in place of the form.


Next: React Part 6: Lists, keys and conditions

reactjavascriptwebdevbeginners

Join WIK to like and reply to DevNotes and see more like this.