React · Lesson 10 of 15
Transitions, Deferred Values and useId
Keep the UI responsive with concurrent features and stable ids.
- Intermediate
- 15 min read
- 3 objectives
Before this lessonLesson 9: Performance: memo, useMemo and useCallback
What you will learn
- Mark an update as a transition
- Defer a heavy value
- Generate stable ids
Your Progress
0 of 15 lessons 0%
- Lessons0 / 15
- Completed0
- Est. time left~ 4 hours
Create a free account to keep your progress on every device.
Some updates must feel instant (typing in a box). Others can wait (filtering a big list). Concurrent React lets you mark the slow work as a transition so the input stays snappy.
useTransition
import { useState, useTransition } from "react";
function Search({ items }) {
const [query, setQuery] = useState("");
const [pending, startTransition] = useTransition();
const [list, setList] = useState(items);
function onChange(e) {
const q = e.target.value;
setQuery(q); // urgent: the input
startTransition(() => {
setList(items.filter((i) => i.includes(q))); // can wait
});
}
return (
<>
<input value={query} onChange={onChange} />
{pending && <p>Updating…</p>}
<ul>{list.map((i) => <li key={i}>{i}</li>)}</ul>
</>
);
}useDeferredValue
When you cannot wrap the setter (a library owns it), defer the value instead. React keeps showing the previous value until the new one is ready.
const deferredQuery = useDeferredValue(query);
const visible = items.filter((i) => i.includes(deferredQuery));useId for labels
Server and client must generate the same id, or hydration mismatches. useId is built for that.
function Field({ label }) {
const id = useId();
return (
<>
<label htmlFor={id}>{label}</label>
<input id={id} />
</>
);
}