# A quick overview of React 19 (my notes)

# 1\. Actions: Making async operations cleaner

## Transition notes

When you wrap an async operation in a transition (using useTransition), React will:

* Immediately set isPending to true
    
* Keep the current UI interactive while the async operation is happening
    
* Apply the state updates only after the async operation completes
    
* Handle pending states and errors automatically
    

## Code sample with useActionState

```javascript
// Old way
function ProfileForm() {
  const [isPending, setIsPending] = useState(false);
  const [error, setError] = useState(null);

  async function handleSubmit() {
    setIsPending(true);
    try {
      await updateProfile();
      setIsPending(false);
    } catch (e) {
      setError(e);
      setIsPending(false);
    }
  }
}

// React 19 way with useActionState
function ProfileForm() {
  const [error, submitAction, isPending] = useActionState(
    async (prev, formData) => {
      try {
        await updateProfile(formData);
        return null; // no error
      } catch (e) {
        return e; // return error
      }
    },
    // initial state
    null
  );

  return (
    <form action={submitAction}>
      {/* Form fields */}
      <button disabled={isPending}>Submit</button>
      {error && <p>{error}</p>}
    </form>
  );
}
```

Async function into its own function. Keep it clean. Just pass the function and then the initial state.

# 2\. Optimistic Updates - Show changes instantly

So nice when error it automatically reverts to original state.

```javascript
function TodoList() {
  // Current state: ["Buy milk"]
  const [optimisticTodos, setOptimisticTodos] = useOptimistic(todos);

  async function addTodo(formData) {
    const newTodo = formData.get("todo");
    // Immediately show: ["Buy milk", "Buy eggs"]
    setOptimisticTodos([...todos, newTodo]);

    // If API fails, React automatically reverts to original state
    await addTodoToServer(newTodo);
  }

  return (
    <form action={addTodo}>
      <input name="todo" />
      <ul>
        {optimisticTodos.map((todo) => (
          <li>{todo}</li>
        ))}
      </ul>
    </form>
  );
}
```

# 3\. The `use` Hook - Handle promises in render

```javascript
function Comments({ commentsPromise }) {
  // This will suspend rendering until comments load
  const comments = use(commentsPromise);

  return (
    <ul>
      {comments.map((comment) => (
        <li key={comment.id}>{comment.text}</li>
      ))}
    </ul>
  );
}

// Use it with Suspense
<Suspense fallback={<Spinner />}>
  <Comments commentsPromise={fetchComments()} />
</Suspense>;
```

"suspending" means temporarily pausing the rendering of a component while it waits for some data or resource to load. When a component suspends, React will show the nearest `<Suspense>` boundary's fallback content until the data is ready.

# 4\. Better ref handling

```javascript
// Old way
const Input = forwardRef((props, ref) => {
  return <input ref={ref} {...props} />;
});

// React 19 way - ref is just a prop!
function Input({ ref, ...props }) {
  return <input ref={ref} {...props} />;
}

// New: Cleanup function for refs
<div
  ref={(node) => {
    if (node) {
      // Setup
      const handler = () => console.log("clicked");
      node.addEventListener("click", handler);

      // Return cleanup
      return () => {
        node.removeEventListener("click", handler);
      };
    }
  }}
/>;
```

Nice that refs have explicit cleanup functions. They run when component unmounts.

# 5\. Simpler Context usage

```javascript
// Old way
const ThemeContext = createContext('light');

<ThemeContext.Provider value="dark">
  <App />
</ThemeContext.Provider>

// React 19 way
<ThemeContext value="dark">
  <App />
</ThemeContext>
```

This is nice to see. Not that I had a big issue with it. But I always appreciate cleaner code.

# 6\. Document Metadata in Components

This is nice especially if you wanna do meta tags dynamically based on the data.

```javascript
function BlogPost({ post }) {
  return (
    <article>
      <title>{post.title}</title>
      <meta name="description" content={post.summary} />
      <link rel="canonical" href={post.url} />

      <h1>{post.title}</h1>
      {post.content}
    </article>
  );
}
```

Since I use [React Router 7 as a framework](https://reactrouter.com/start/framework/route-module), this is handled for me there when using it lol.
