MENU
The Standalone startTransition
useTransition() is a Hook, so it can only be called inside a component or a custom Hook. React also exports a plain startTransition function (not a Hook) that schedules a Transition the exact same way, for use anywhere a Hook isn't allowed – inside a data library, an event listener set up outside React, or any other plain function. The tradeoff: without a Hook call site of its own, it has nowhere to report back an isPending flag.
Both buttons update the very same counter, wrapped as a Transition either way, so both take about as long to actually apply. Only the button that goes through the Hook ever shows a pending indicator: isPending reflects Transitions started through this component's own useTransition() call specifically, not just any Transition happening anywhere.
RESETRUNFULL
RESETRUNFULL
<!DOCTYPE html><html><head>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head><body>
<div></div>
<script type="text/babel">
function SlowValue({ n }) {
const items = [];
for (let i = 0; i < 300; i++) {
const start = performance.now();
while (performance.now() - start < 1) {} // simulate expensive work
items.push(<li key={i}>{n} #{i}</li>);
}
return <ul>{items}</ul>;
}
function App() {
const [n, setN] = React.useState(0);
const [isPending, startTransition] = React.useTransition();
function incrementViaHook() {
startTransition(() => setN(n + 1));
}
function incrementViaStandalone() {
// Imagine this running outside any component or Hook (eg. inside a
// WebSocket handler that lives in a plain data module) -- useTransition
// itself wouldn't be callable there, but the standalone function is.
React.startTransition(() => setN(n + 1));
}
return (
<React.Fragment>
<button onClick={incrementViaHook}>Hook: {n}{isPending ? ' (pending...)' : ''}</button>
<button onClick={incrementViaStandalone}>Standalone: {n}</button>
<SlowValue n={n} />
</React.Fragment>
);}
const root = ReactDOM.createRoot(document.querySelector("div"));
root.render(<App/>);
</script>
</body></html>In real code, a function living outside a component wouldn't have setN in scope like this – it would more likely reach it through a module-level reference, a ref, or a store. The inline closure above is simplified for the demo; the scheduling behavior is exactly what you'd see in that more realistic setup.