State

  • re-renders the component when something changes
  • should not be used for values that do not directly affect the UI

Refs

  • do not cause a re-evaluation of the component when something changes.
  • can be used to gain direct access to DOM elements (→ great for reading values or accessing certain browser APIs).

Example with “User Tic-Tac-Toe”

  • Implementation with useState
//Player.jsx with state
export default function Player() {
  const [playerName, setPlayerName] = useState(null);
  const [submitted, setSubmitted] = useState(false);


  function handleChange(event) {
    setSubmitted(false);
    setPlayerName(event.target.value);
  }

  
  function handleClick() {
    setPlayerName(playerNameRef.current.value);
  }

  return (
    <section id="player">
      <h2>Welcome {submitted ? playerName : ' unknown entity'}</h2>
      <p>
        <input type="text" onChange={handleChange} value={playerName}/>
        <button onClick={handleClick}>Set Name</button>
      </p>
    </section>
  );
}
  • Implementation with useRef
//Player.jsx with ref
import { useRef } from "react";
import { useState } from "react";

export default function Player() {
  const playerNameRef = useRef();
  const [playerName, setPlayerName] = useState(null);
  function handleClick() {
    setPlayerName(playerNameRef.current.value);
  }
  return (
    <section id="player">
      <h2>Welcome {playerName ?? 'unknown entity'}</h2>
      <p>
        <input type="text" ref={playerNameRef}/>
        <button onClick={handleClick}>Set Name</button>
      </p>
    </section>
  );
}

Deleting a Form Outside of a Component

//App.jsx
import { useRef } from "react";
import Form from "./Form";
function App() {
  const form = useRef();
  function handleRestart() {
    form.current.clear();
  }
  return (
    <div id="app">
      <button onClick={handleRestart}>Restart</button>
      <Form ref={form}/>
    </div>
  );
}
export default App
//Form.jsx
import { forwardRef, useImperativeHandle, useRef } from "react";
const Form = forwardRef(function Form(props, ref) {
  const form = useRef();
  useImperativeHandle(ref, () => {
    return {
      clear() {
        form.current.reset();
      },
    };
  });
  return (
    <form ref={form}>
      <p>
        <label>Name</label>
        <input type="text" />
      </p>
      <p>
        <label>Email</label>
        <input type="email" />
      </p>
      <p id="actions">
        <button>Save</button>
      </p>
    </form>
  );
});

export default Form;