TL;DR: React 19 вводит радикальные изменения в работу с асинхронностью — хуки use(), Server Actions и оптимистичные обновления fundamentally меняют подход к data fetching и side effects. useEffect становится legacy для многих сценариев.
Контекст: почему useEffect больше не король
Последние 5 лет useEffect был swiss army knife для side effects в React. Но в 2024-м этот подход показывает свою устарелость:
// Old-school data fetching
function UserProfile({ id }) {
const [user, setUser] = useState(null);
useEffect(() => {
let ignore = false;
fetch(`/api/users/${id}`)
.then(res => res.json())
.then(data => !ignore && setUser(data));
return () => { ignore = true };
}, [id]);
return user ? <Profile data={user} /> : <Spinner />;
}
Проблемы классического подхода:
- Race conditions (нужен ignore-флаг)
- Нет встроенной отмены запросов
- Сложность в обработке ошибок
- Неочевидные ререндеры
Новый мир: use() и Server Actions
React 19 предлагает альтернативы через Suspense-совместимые API:
// Data fetching с use()
function UserProfile({ id, userPromise }) {
const user = use(userPromise); // Suspense-совместимый хук
return <Profile data={user} />;
}
// Обёртка для Suspense boundary
function UserProfileWrapper({ id }) {
const userPromise = fetch(`/api/users/${id}`).then(res => res.json());
return (
<Suspense fallback={<Spinner />}>
<UserProfile id={id} userPromise={userPromise} />
</Suspense>
);
}
Что изменилось:
- Нет ручного управления состоянием загрузки
- Автоматическая отмена при размонтировании
- Интеграция с Concurrent Features
Server Actions: RPC для фронтенда
// Серверный action
async function updateUser(formData) {
'use server';
const user = await db.users.update({
id: formData.get('id'),
name: formData.get('name')
});
return user;
}
// Клиентский компонент
function UserForm() {
return (
<form action={updateUser}>
<input type="hidden" name="id" value="123" />
<input name="name" defaultValue="John" />
<button type="submit">Save</button>
</form>
);
}
Фичи:
- Zero-boilerplate RPC
- Автоматическая сериализация/десериализация
- Интеграция с формой без useEffect
Оптимистичные обновления без боли
Комбинация useOptimistic и useFormStatus решает classic UI problem:
function MessageForm() {
const [messages, setMessages] = useState([]);
const [optimisticMessages, addOptimisticMessage] = useOptimistic(
messages,
(state, newMessage) => [...state, { text: newMessage, sending: true }]
);
async function formAction(formData) {
const message = formData.get('message');
addOptimisticMessage(message);
await sendMessage(message); // Server Action
setMessages(prev => [...prev, { text: message }]);
}
const { pending } = useFormStatus();
return (
<>
<ul>
{optimisticMessages.map((msg, i) => (
<li key={i}>{msg.text} {msg.sending && '...'}</li>
))}
</ul>
<form action={formAction}>
<input name="message" disabled={pending} />
<button disabled={pending}>Send</button>
</form>
</>
);
}
Когда useEffect ещё нужен?
Несмотря на нововведения, useEffect остаётся relevant для:
- Интеграции с не-React библиотеками (например, D3.js)
- Подписки на внешние события (WebSocket, window.resize)
- Сложных side effects, не связанных с данными
// Пример валидного использования
function useWindowSize() {
const [size, setSize] = useState({
width: window.innerWidth,
height: window.innerHeight
});
useEffect(() => {
const handler = () => setSize({
width: window.innerWidth,
height: window.innerHeight
});
window.addEventListener('resize', handler);
return () => window.removeEventListener('resize', handler);
}, []);
return size;
}
Миграционная стратегия
- Data fetching: Переход с useEffect → use() + Suspense
- Формы: Замена onSubmit-эффектов → Server Actions
- Оптимистичные UI: useOptimistic вместо ручных состояний
- Глобальное состояние: Рассмотреть React Query/SWR для complex cases
Вывод
React 19 — это paradigm shift в управлении асинхронностью. 80% случаев использования useEffect теперь можно заменить более специализированными API. Главные benefits:
- Уменьшение boilerplate кода
- Предсказуемое поведение
- Лучшая интеграция с Concurrent Mode
- Более декларативные паттерны
Для legacy-проектов переход будет постепенным, но новые приложения стоит сразу строить на обновлённой ментальной модели.
Источник: https://www.reddit.com/r/reactjs/comments/1ri0vkg/tried_explaining_how_react_19_reduces_useeffect/