Voltar para o Blog

React 19.2: Activity Component e useEffectEvent Revolucionam o Desenvolvimento

Olá HaWkers, o React continua evoluindo e a versão 19.2, lançada em outubro de 2025, traz duas novidades que prometem mudar a forma como construímos aplicações: o Activity component e o hook useEffectEvent. Essas adições resolvem problemas que desenvolvedores enfrentam há anos.

Você já teve dificuldades em gerenciar o pré-carregamento de conteúdo ou se frustrou com as complexidades do array de dependências do useEffect? Então este artigo é para você.

O Que Há de Novo no React 19.2

Esta é a terceira release significativa do ano, seguindo o React 19 em dezembro de 2024 e o React 19.1 em junho de 2025. O time do React focou em dois problemas fundamentais da experiência do desenvolvedor.

Activity Component: O Futuro do Pre-Rendering

O novo componente <Activity /> permite dividir sua aplicação em "atividades" que podem ser controladas e priorizadas. É uma alternativa ao rendering condicional tradicional, oferecendo dois modos: visible e hidden.

Por Que o Activity é Revolucionário

Tradicionalmente, quando você precisa renderizar conteúdo condicionalmente, usa algo assim:

// Abordagem tradicional - o componente é destruído e recriado
function Dashboard() {
  const [activeTab, setActiveTab] = useState('overview');

  return (
    <div>
      <nav>
        <button onClick={() => setActiveTab('overview')}>Overview</button>
        <button onClick={() => setActiveTab('analytics')}>Analytics</button>
        <button onClick={() => setActiveTab('settings')}>Settings</button>
      </nav>

      {/* Cada mudança de tab destrói o componente anterior */}
      {activeTab === 'overview' && <OverviewPanel />}
      {activeTab === 'analytics' && <AnalyticsPanel />}
      {activeTab === 'settings' && <SettingsPanel />}
    </div>
  );
}

O problema com essa abordagem é que cada troca de tab destrói o componente anterior e cria um novo, perdendo todo o estado interno e exigindo novas requisições de dados.

A Solução com Activity

Com o Activity component, você pode manter os componentes montados mas ocultos:

import { Activity } from 'react';

function Dashboard() {
  const [activeTab, setActiveTab] = useState('overview');

  return (
    <div>
      <nav>
        <button onClick={() => setActiveTab('overview')}>Overview</button>
        <button onClick={() => setActiveTab('analytics')}>Analytics</button>
        <button onClick={() => setActiveTab('settings')}>Settings</button>
      </nav>

      {/* Componentes permanecem montados, apenas visibilidade muda */}
      <Activity mode={activeTab === 'overview' ? 'visible' : 'hidden'}>
        <OverviewPanel />
      </Activity>

      <Activity mode={activeTab === 'analytics' ? 'visible' : 'hidden'}>
        <AnalyticsPanel />
      </Activity>

      <Activity mode={activeTab === 'settings' ? 'visible' : 'hidden'}>
        <SettingsPanel />
      </Activity>
    </div>
  );
}

Benefícios do Activity Component

1. Preservação de Estado
Inputs preenchidos, scroll position e estado interno são mantidos quando você navega entre activities.

2. Pre-fetching Inteligente
Você pode renderizar partes ocultas da aplicação que o usuário provavelmente acessará:

function ProductPage({ productId }) {
  const [showReviews, setShowReviews] = useState(false);

  return (
    <div>
      <ProductDetails id={productId} />

      <button onClick={() => setShowReviews(true)}>
        Ver Avaliações
      </button>

      {/* Reviews são carregados em background */}
      <Activity mode={showReviews ? 'visible' : 'hidden'}>
        <ReviewsSection productId={productId} />
      </Activity>
    </div>
  );
}

3. Navegação Instantânea
Back navigations se tornam instantâneas porque o estado anterior ainda existe na memória.

useEffectEvent: Adeus Dependency Hell

O hook useEffectEvent resolve uma das maiores dores de cabeça do React: gerenciar as dependências do useEffect quando você precisa de valores atualizados sem re-executar o efeito.

O Problema Clássico

Considere este cenário comum:

// Problema: analytics dispara múltiplas vezes desnecessariamente
function ProductPage({ productId, category, user }) {
  useEffect(() => {
    // Queremos logar apenas quando productId muda
    // Mas precisamos de category e user.id para o log
    analytics.logProductView({
      productId,
      category,
      userId: user.id,
      timestamp: Date.now()
    });
  }, [productId, category, user.id]); // Problema: dispara se category ou user mudar

  return <ProductDetails id={productId} />;
}

Soluções Antigas (Problemáticas)

Desenvolvedores costumavam usar ref para contornar isso:

// Solução antiga com refs - funciona mas é verbosa
function ProductPage({ productId, category, user }) {
  const categoryRef = useRef(category);
  const userRef = useRef(user);

  useLayoutEffect(() => {
    categoryRef.current = category;
    userRef.current = user;
  });

  useEffect(() => {
    analytics.logProductView({
      productId,
      category: categoryRef.current,
      userId: userRef.current.id,
      timestamp: Date.now()
    });
  }, [productId]);

  return <ProductDetails id={productId} />;
}

A Solução com useEffectEvent

O novo hook torna isso elegante e declarativo:

import { useEffect, useEffectEvent } from 'react';

function ProductPage({ productId, category, user }) {
  // Cria uma função estável que sempre acessa valores atualizados
  const logProductView = useEffectEvent(() => {
    analytics.logProductView({
      productId,
      category,     // Sempre o valor mais recente
      userId: user.id,  // Sempre o valor mais recente
      timestamp: Date.now()
    });
  });

  useEffect(() => {
    // logProductView não precisa estar nas dependências
    // mas sempre usa os valores mais recentes de props
    logProductView();
  }, [productId]); // Dispara apenas quando productId muda

  return <ProductDetails id={productId} />;
}

Casos de Uso Práticos do useEffectEvent

1. Event Handlers em Effects

function ChatRoom({ roomId, onMessage }) {
  // onMessage pode mudar, mas não queremos reconectar
  const handleMessage = useEffectEvent((message) => {
    onMessage(message);
  });

  useEffect(() => {
    const connection = createConnection(roomId);

    connection.on('message', handleMessage);
    connection.connect();

    return () => connection.disconnect();
  }, [roomId]); // Reconecta apenas quando roomId muda
}

2. Logging e Analytics

function SearchResults({ query, filters, results }) {
  const logSearch = useEffectEvent(() => {
    analytics.logSearch({
      query,
      filterCount: Object.keys(filters).length,
      resultCount: results.length,
      timestamp: Date.now()
    });
  });

  useEffect(() => {
    // Loga apenas quando query muda
    // mas inclui dados atualizados de filters e results
    logSearch();
  }, [query]);

  return <ResultsList results={results} />;
}

3. Sincronização com APIs Externas

function VideoPlayer({ videoId, volume, playbackRate }) {
  const updatePlayerSettings = useEffectEvent(() => {
    // Configurações que não devem causar reload do vídeo
    player.setVolume(volume);
    player.setPlaybackRate(playbackRate);
  });

  useEffect(() => {
    const player = createPlayer(videoId);

    // Aplica configurações iniciais
    updatePlayerSettings();

    return () => player.destroy();
  }, [videoId]); // Recarrega vídeo apenas quando videoId muda

  // Atualiza configurações sem recarregar
  useEffect(() => {
    updatePlayerSettings();
  }, [volume, playbackRate]);
}

React Performance Tracks no DevTools

O React 19.2 também adiciona novas tracks de performance no Chrome DevTools que facilitam muito o debugging.

Scheduler Track

Mostra o que o React está processando em diferentes prioridades:

function App() {
  const [searchTerm, setSearchTerm] = useState('');
  const [results, setResults] = useState([]);

  const handleSearch = (term) => {
    // Prioridade alta - atualização imediata do input
    setSearchTerm(term);

    // Prioridade baixa - busca não bloqueia UI
    startTransition(() => {
      const filtered = performExpensiveSearch(term);
      setResults(filtered);
    });
  };

  return (
    <div>
      <input
        value={searchTerm}
        onChange={(e) => handleSearch(e.target.value)}
      />
      <SearchResults results={results} />
    </div>
  );
}

No DevTools, você verá tracks separadas para:

  • Blocking: Interações de usuário (alta prioridade)
  • Transition: Updates dentro de startTransition
  • Idle: Trabalho de baixa prioridade

Migração e Compatibilidade

Atualizando para React 19.2

A atualização é simples e não possui breaking changes em relação ao React 19.1:

# npm
npm install react@19.2.0 react-dom@19.2.0

# yarn
yarn add react@19.2.0 react-dom@19.2.0

# pnpm
pnpm add react@19.2.0 react-dom@19.2.0

Compatibilidade de Tipos TypeScript

Para TypeScript, certifique-se de atualizar os tipos:

npm install @types/react@19.2.0 @types/react-dom@19.2.0

Os novos tipos incluem definições completas para Activity e useEffectEvent:

import { Activity, useEffectEvent } from 'react';

interface ActivityProps {
  mode: 'visible' | 'hidden';
  children: React.ReactNode;
}

// useEffectEvent preserva o tipo da função
const handler = useEffectEvent((event: MouseEvent) => {
  // event é tipado corretamente
  console.log(event.clientX, event.clientY);
});

Comparação com Abordagens Anteriores

Activity vs CSS display: none

Aspecto Activity hidden CSS display: none
React Lifecycle Pausado Continua executando
Memory Otimizado Estado mantido
Effects Não executam Continuam executando
Re-render Não ocorre Pode ocorrer

useEffectEvent vs useCallback

Aspecto useEffectEvent useCallback
Valores Sempre atualizados Stale se deps mudar
Dependências Nunca listado Deve ser listado
Caso de uso Effects Props de componentes
Estabilidade Sempre estável Depende de deps

Conclusão

O React 19.2 representa mais um passo importante na evolução do framework. O Activity component resolve elegantemente o problema de pré-carregamento e preservação de estado, enquanto o useEffectEvent elimina anos de frustração com dependency arrays.

Essas novas ferramentas não são apenas melhorias incrementais - elas mudam fundamentalmente como pensamos sobre gerenciamento de estado e efeitos colaterais em aplicações React.

Se você está trabalhando com React, recomendo começar a experimentar essas features em projetos menores para entender seu potencial. Para complementar seu conhecimento sobre performance, confira nosso artigo sobre ECMAScript 2025 e as Novas Features do JavaScript.

Bora pra cima! 🦅

Comentários (0)

Esse artigo ainda não possui comentários 😢. Seja o primeiro! 🚀🦅

Adicionar comentário