Первый хук с которого начнем это useTransition . Он используется для обработки состояния ожидания.

Хук useTransition возвращает:

const [isPending, startTransition] = useTransition();

useTransition(async () => {
	// code to handle form submitting
})

return (
	// ...
	<button onClick={handleSubmit} disabled={isPending}>
	  Update
	</button>
	// ...
)

То есть теперь вместо состояния const [isPending, setIsPending] = useState(false); которое мы вручную меняли сначала на true, а по завершению работы асинхронной функции на false, мы можем использовать хук useTransition().

Хук useTransition() автоматически следит за выполнением асинхронной функции переданной в startTransition(). Когда она стартовала - переводит isPending в true, а по её завершению - в false.

Это избавляет нас от необходимости вручную управлять состоянием загрузки.

Вот как это выглядит:

const [isPending, setTransition] = useTransition();

const handleSubmit = () => {
	setTransition( async ()=>{
		const error = await updateName(name);
		if (error) {
			setError(error);
			return;
		}
		setSuccess(true);
	})
};

return (
	<button onClick={handleSubmit} disabled={isPending}>
	  Update
	</button>
)

Полный код компонента:

import { useState, useTransition } from "react";

async function updateName() {
	await new Promise((resolve) => {
		setTimeout(() => {
			resolve();
		}, 2000);
	});
	console.log("Name updated!");
}

function App() {
	const [name, setName] = useState("");
	const [error, setError] = useState(null);
	
	const [isPending, setTransition] = useTransition();

	const [success, setSuccess] = useState(false)

	const handleSubmit = () => {
		setTransition( async ()=>{
			const error = await updateName(name);
			if (error) {
				setError(error);
				return;
			}
			setSuccess(true);
		})
	};

	function showSuccess(){
		return (
			<div className="alert alert-success mb-3">
				Form submited!
			</div>
		);
	}

	return (
		<div className="card shadow p-3">
			{success && showSuccess()}
			<label className="form-label">Enter name</label>
			<input
				className="form-control mb-3"
				value={name}
				onChange={(event) => setName(event.target.value)}
			/>
			<button
				className="btn btn-dark"
				onClick={handleSubmit}
				disabled={isPending}
			>
				Update
			</button>
			{error && <p>{error}</p>}
		</div>
	);
}

export default App;

Как видите, код стал намного чище. Мы избавились от ручного управления состоянием загрузки, и теперь React сам заботится об этом через хук useTransition.

<aside> 💡

Примечание

По соглашению функции, использующие асинхронные переходы, называются «actions»

Actions функции автоматически управляют отправкой данных для вас:

О actions мы поговорим далее </aside>