React 19: Эволюция асинхронности и смерть useEffect?

#react#react19#hooks#async

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 />;
}

Проблемы классического подхода:

Новый мир: 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>
  );
}

Что изменилось:

  1. Нет ручного управления состоянием загрузки
  2. Автоматическая отмена при размонтировании
  3. Интеграция с 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>
  );
}

Фичи:

Оптимистичные обновления без боли

Комбинация 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 для:

// Пример валидного использования
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;
}

Миграционная стратегия

  1. Data fetching: Переход с useEffect → use() + Suspense
  2. Формы: Замена onSubmit-эффектов → Server Actions
  3. Оптимистичные UI: useOptimistic вместо ручных состояний
  4. Глобальное состояние: Рассмотреть React Query/SWR для complex cases

Вывод

React 19 — это paradigm shift в управлении асинхронностью. 80% случаев использования useEffect теперь можно заменить более специализированными API. Главные benefits:

Для legacy-проектов переход будет постепенным, но новые приложения стоит сразу строить на обновлённой ментальной модели.


Источник: https://www.reddit.com/r/reactjs/comments/1ri0vkg/tried_explaining_how_react_19_reduces_useeffect/