Guia de Upgrade do React 19

25 de abril de 2024 por Ricky Hanlon


As melhorias adicionadas ao React 19 exigem algumas mudanças radicais, mas trabalhamos para tornar o upgrade o mais tranquilo possível e não esperamos que as mudanças afetem a maioria dos apps.

Note

React 18.3 também foi publicado

Para ajudar a facilitar o upgrade para o React 19, publicamos uma versão react@18.3 que é idêntica à 18.2, mas adiciona avisos para APIs descontinuadas e outras mudanças que são necessárias para o React 19.

Recomendamos que você atualize para o React 18.3 primeiro para ajudar a identificar quaisquer problemas antes de atualizar para o React 19.

Para obter uma lista de alterações no 18.3, consulte as Notas da versão.

Neste post, vamos guiá-lo pelas etapas para o upgrade para o React 19:

Se você quiser nos ajudar a testar o React 19, siga as etapas neste guia de upgrade e relate quaisquer problemas que encontrar. Para obter uma lista de novos recursos adicionados ao React 19, consulte o post de lançamento do React 19.


Instalação

Note

A nova Transformação JSX agora é obrigatória

Apresentamos uma nova transformação JSX em 2020 para melhorar o tamanho do bundle e usar o JSX sem importar o React. No React 19, estamos adicionando melhorias adicionais, como usar ref como uma prop e melhorias de velocidade do JSX que exigem a nova transformação.

Se a nova transformação não estiver habilitada, você verá este aviso:

Console
Seu app (ou uma de suas dependências) está usando uma transformação JSX desatualizada. Atualize para a transformação JSX moderna para obter desempenho mais rápido: https://react.dev/link/new-jsx-transform

Esperamos que a maioria dos apps não seja afetada, pois a transformação já está habilitada na maioria dos ambientes. Para obter instruções manuais sobre como atualizar, consulte o post de anúncio.

Para instalar a versão mais recente do React e React DOM:

npm install --save-exact react@^19.0.0 react-dom@^19.0.0

Ou, se você estiver usando Yarn:

yarn add --exact react@^19.0.0 react-dom@^19.0.0

Se você estiver usando TypeScript, também precisará atualizar os types.

npm install --save-exact @types/react@^19.0.0 @types/react-dom@^19.0.0

Ou, se você estiver usando Yarn:

yarn add --exact @types/react@^19.0.0 @types/react-dom@^19.0.0

Também estamos incluindo um codemod para as substituições mais comuns. Consulte Mudanças no TypeScript abaixo.

Codemods

Para ajudar com o upgrade, trabalhamos com a equipe do codemod.com para publicar codemods que atualizarão automaticamente seu código para muitas das novas APIs e padrões no React 19.

Todos os codemods estão disponíveis no repositório react-codemod e a equipe do Codemod se juntou para ajudar a manter os codemods. Para executar esses codemods, recomendamos usar o comando codemod em vez de react-codemod porque ele é executado mais rápido, lida com migrações de código mais complexas e oferece melhor suporte para TypeScript.

Note

Execute todos os codemods do React 19

Execute todos os codemods listados neste guia com a receita codemod do React 19:

npx codemod@latest react/19/migration-recipe

Isso executará os seguintes codemods de react-codemod:

Isso não inclui as mudanças do TypeScript. Consulte Mudanças no TypeScript abaixo.

As mudanças que incluem um codemod incluem o comando abaixo.

Para obter uma lista de todos os codemods disponíveis, consulte o repositório react-codemod.

Breaking changes

Erros em render não são lançados novamente

Em versões anteriores do React, os erros lançados durante o render eram capturados e lançados novamente. Em DEV, também registraríamos no console.error, resultando em logs de erros duplicados.

No React 19, melhoramos a forma como os erros são tratados para reduzir a duplicação, não lançando novamente:

  • Erros não detectados: Os erros que não são detectados por limites de erro são relatados para window.reportError.
  • **Erros detectados: Os erros que são detectados por limites de erro são relatados para console.error.

Esta mudança não deve afetar a maioria dos apps, mas se seu relatório de erros de produção depender de erros sendo lançados novamente, você pode precisar atualizar o tratamento de erros. Para dar suporte a isso, adicionamos novos métodos a createRoot e hydrateRoot para tratamento de erros personalizado:

const root = createRoot(container, {
onUncaughtError: (error, errorInfo) => {
// ... log error report
},
onCaughtError: (error, errorInfo) => {
// ... log error report
}
});

Para obter mais informações, consulte a documentação para createRoot e hydrateRoot.

APIs React descontinuadas removidas

Removido: propTypes e defaultProps para funções

PropTypes foram descontinuados em Abril de 2017 (v15.5.0).

No React 19, estamos removendo as verificações propType do pacote React, e seu uso será silenciosamente ignorado. Se você estiver usando propTypes, recomendamos migrar para TypeScript ou outra solução de verificação de tipo.

Também estamos removendo defaultProps de componentes de função em vez de parâmetros padrão ES6. Componentes de classe continuarão a dar suporte a defaultProps , pois não há alternativa ES6.

// Antes
import PropTypes from 'prop-types';

function Heading({text}) {
return <h1>{text}</h1>;
}
Heading.propTypes = {
text: PropTypes.string,
};
Heading.defaultProps = {
text: 'Hello, world!',
};
// Depois
interface Props {
text?: string;
}
function Heading({text = 'Hello, world!'}: Props) {
return <h1>{text}</h1>;
}

Note

Codemod propTypes para TypeScript com:

npx codemod@latest react/prop-types-typescript

Removido: Contexto Legado usando contextTypes e getChildContext

O Contexto Legado foi descontinuado em Outubro de 2018 (v16.6.0).

O Contexto Legado só estava disponível em componentes de classe usando as APIs contextTypes e getChildContext, e foi substituído por contextType devido a bugs sutis que eram fáceis de ignorar. No React 19, estamos removendo o Contexto Legado para tornar o React um pouco menor e mais rápido.

Se você ainda estiver usando o Contexto Legado em componentes de classe, precisará migrar para a nova API contextType:

// Antes
import PropTypes from 'prop-types';

class Parent extends React.Component {
static childContextTypes = {
foo: PropTypes.string.isRequired,
};

getChildContext() {
return { foo: 'bar' };
}

render() {
return <Child />;
}
}

class Child extends React.Component {
static contextTypes = {
foo: PropTypes.string.isRequired,
};

render() {
return <div>{this.context.foo}</div>;
}
}
// Depois
const FooContext = React.createContext();

class Parent extends React.Component {
render() {
return (
<FooContext value='bar'>
<Child />
</FooContext>
);
}
}

class Child extends React.Component {
static contextType = FooContext;

render() {
return <div>{this.context}</div>;
}
}

Removido: refs de string

Refs de string foram descontinuados em Março de 2018 (v16.3.0).

Componentes de classe suportavam refs de string antes de serem substituídos por ref callbacks devido a múltiplas desvantagens. No React 19, estamos removendo refs de string para tornar o React mais simples e fácil de entender.

Se você ainda estiver usando refs de string em componentes de classe, precisará migrar para ref callbacks:

// Antes
class MyComponent extends React.Component {
componentDidMount() {
this.refs.input.focus();
}

render() {
return <input ref='input' />;
}
}
// Depois
class MyComponent extends React.Component {
componentDidMount() {
this.input.focus();
}

render() {
return <input ref={input => this.input = input} />;
}
}

Note

Codemod refs de string com ref callbacks:

npx codemod@latest react/19/replace-string-ref

Removido: factories de padrão de módulo

Factories de padrão de módulo foram descontinuadas em Agosto de 2019 (v16.9.0).

Este padrão raramente era usado e dar suporte a ele faz com que o React seja ligeiramente maior e mais lento do que o necessário. No React 19, estamos removendo o suporte para factories de padrão de módulo, e você precisará migrar para funções regulares:

// Antes
function FactoryComponent() {
return { render() { return <div />; } }
}
// Depois
function FactoryComponent() {
return <div />;
}

Removido: React.createFactory

createFactory foi descontinuado em Fevereiro de 2020 (v16.13.0).

Usar createFactory era comum antes do suporte amplo para JSX, mas é raramente usado hoje em dia e pode ser substituído por JSX. No React 19, estamos removendo createFactory, e você precisará migrar para JSX:

// Antes
import { createFactory } from 'react';

const button = createFactory('button');
// Depois
const button = <button />;

Removido: react-test-renderer/shallow

No React 18, atualizamos react-test-renderer/shallow para reexportar react-shallow-renderer. No React 19, estamos removendo react-test-render/shallow para preferir a instalação direta do pacote:

npm install react-shallow-renderer --save-dev
- import ShallowRenderer from 'react-test-renderer/shallow';
+ import ShallowRenderer from 'react-shallow-renderer';

Note

Por favor, reconsidere o renderização raso

A renderização rasa depende dos detalhes internos do React e pode impedi-lo de upgrades futuros. Recomendamos migrar seus testes para @testing-library/react ou @testing-library/react-native.

APIs React DOM descontinuadas removidas

Removido: react-dom/test-utils

Movemos act de react-dom/test-utils para o pacote react:

Console
ReactDOMTestUtils.act foi descontinuado em favor de React.act. Importe act de react em vez de react-dom/test-utils. Consulte https://react.dev/warnings/react-dom-test-utils para obter mais informações.

Para corrigir este aviso, você pode importar act de react:

- import {act} from 'react-dom/test-utils'
+ import {act} from 'react';

Todas as outras funções test-utils foram removidas. Essas utilidades eram incomuns e tornavam muito fácil depender de detalhes de implementação de baixo nível de seus componentes e do React. No React 19, essas funções darão erro quando chamadas e suas exportações serão removidas em uma versão futura.

Consulte a página de aviso para obter alternativas.

Note

Codemod de ReactDOMTestUtils.act para React.act:

npx codemod@latest react/19/replace-act-import

Removido: ReactDOM.render

ReactDOM.render foi descontinuado em Março de 2022 (v18.0.0). No React 19, estamos removendo ReactDOM.render, e você precisará migrar para usar ReactDOM.createRoot:

// Antes
import {render} from 'react-dom';
render(<App />, document.getElementById('root'));

// Depois
import {createRoot} from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);

Note

Codemod de ReactDOM.render para ReactDOMClient.createRoot:

npx codemod@latest react/19/replace-reactdom-render

Removido: ReactDOM.hydrate

ReactDOM.hydrate foi descontinuado em Março de 2022 (v18.0.0). No React 19, estamos removendo ReactDOM.hydrate, e você precisará migrar para usar ReactDOM.hydrateRoot,

// Antes
import {hydrate} from 'react-dom';
hydrate(<App />, document.getElementById('root'));

// Depois
import {hydrateRoot} from 'react-dom/client';
hydrateRoot(document.getElementById('root'), <App />);

Note

Codemod de ReactDOM.hydrate para ReactDOMClient.hydrateRoot:

npx codemod@latest react/19/replace-reactdom-render

Removido: unmountComponentAtNode

ReactDOM.unmountComponentAtNode foi descontinuado em Março de 2022 (v18.0.0). No React 19, você precisará migrar para usar root.unmount().

// Antes
unmountComponentAtNode(document.getElementById('root'));

// Depois
root.unmount();

For more see root.unmount() for createRoot and hydrateRoot.

Note

Codemod unmountComponentAtNode to root.unmount:

npx codemod@latest react/19/replace-reactdom-render

Removido: ReactDOM.findDOMNode

ReactDOM.findDOMNode foi descontinuado em Outubro de 2018 (v16.6.0).

Estamos removendo findDOMNode porque era uma exceção legada que era lenta para executar, frágil para refatorar, só retornava o primeiro filho e quebrava os níveis de abstração (veja mais aqui). Você pode substituir ReactDOM.findDOMNode com refs do DOM:

// Antes
import {findDOMNode} from 'react-dom';

function AutoselectingInput() {
useEffect(() => {
const input = findDOMNode(this);
input.select()
}, []);

return <input defaultValue="Hello" />;
}
// Depois
function AutoselectingInput() {
const ref = useRef(null);
useEffect(() => {
ref.current.select();
}, []);

return <input ref={ref} defaultValue="Hello" />
}

Novas descontinuações

Descontinuado: element.ref

React 19 suporta ref como uma prop, então estamos descontinuando o element.ref em favor de element.props.ref.

Acessar element.ref irá avisar:

Console
Acessar element.ref não é mais suportado. ref agora é uma prop normal. Ele será removido do tipo Elemento JSX em uma versão futura.

Descontinuado: react-test-renderer

Estamos descontinuando react-test-renderer pois ele implementa seu próprio ambiente de renderização que não corresponde ao ambiente que os usuários usam, promove detalhes de implementação de teste e depende da introspecção dos detalhes internos do React.

O renderizador de teste foi criado antes que houvesse estratégias de teste mais viáveis ​​disponíveis, como React Testing Library, e agora recomendamos o uso de uma biblioteca de teste moderna em vez disso.

No React 19, react-test-renderer registra um aviso de descontinuação e mudou para renderização concorrente. Recomendamos migrar seus testes para @testing-library/react ou @testing-library/react-native para uma experiência de teste moderna e bem suportada.

Mudanças notáveis

Mudanças no StrictMode

React 19 inclui várias correções e melhorias no Strict Mode.

Ao renderizar duas vezes no Strict Mode em desenvolvimento, useMemo e useCallback reutilizarão os resultados memorizados da primeira renderização durante a segunda renderização. Componentes que já são compatíveis com o Strict Mode não devem notar uma diferença no comportamento.

Como acontece com todos os comportamentos do Strict Mode, esses recursos são projetados para revelar proativamente erros em seus componentes durante o desenvolvimento para que você possa corrigi-los antes de serem enviados para produção. Por exemplo, durante o desenvolvimento, o Strict Mode irá invocar duas vezes as funções de callback de ref na montagem inicial, para simular o que acontece quando um componente montado é substituído por um fallback de Suspense.

Melhorias no Suspense

No React 19, quando um componente suspende, o React irá commitar imediatamente o fallback do limite de Suspense mais próximo sem esperar que toda a árvore de irmãos seja renderizada. Depois que o fallback fizer o commit, o React agenda outra renderização para os irmãos suspensos para “pré-aquecer” as requisições lazy no resto da árvore:

Diagrama mostrando uma árvore de três componentes, um pai rotulado de Accordion (Acordeão) e dois filhos rotulados de Panel (Painel). Ambos os componentes Panel contêm isActive com valor false.
Diagrama mostrando uma árvore de três componentes, um pai rotulado de Accordion (Acordeão) e dois filhos rotulados de Panel (Painel). Ambos os componentes Panel contêm isActive com valor false.

Anteriormente, quando um componente suspendia, os irmãos suspensos eram renderizados e então o fallback era commited.

O mesmo diagrama do anterior, com o isActive do primeiro componente filho Panel evidenciado, indicando um clique com o valor isActive definido como true. O segundo componente Panel ainda contém o valor false.
O mesmo diagrama do anterior, com o isActive do primeiro componente filho Panel evidenciado, indicando um clique com o valor isActive definido como true. O segundo componente Panel ainda contém o valor false.

No React 19, quando um componente suspende, o fallback é commited e então os irmãos suspensos são renderizados.

Essa mudança significa que os fallbacks do Suspense exibem mais rapidamente, enquanto ainda aquecem as requisições lazy na árvore suspensa.

Builds UMD removidos

UMD foi amplamente utilizado no passado como uma maneira conveniente de carregar o React sem uma etapa de build. Agora, existem alternativas modernas para carregar módulos como scripts em documentos HTML. Começando com o React 19, o React não produzirá mais builds UMD para reduzir a complexidade de seu processo de teste e lançamento.

Para carregar o React 19 com uma tag de script, recomendamos o uso de um CDN baseado em ESM, como esm.sh.

<script type="module">
import React from "https://esm.sh/react@19/?dev"
import ReactDOMClient from "https://esm.sh/react-dom@19/client?dev"
...
</script>

Bibliotecas que dependem de internos do React podem bloquear atualizações

Esta versão inclui alterações nos internos do React que podem impactar bibliotecas que ignoram nossos apelos para não usar internos como SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED. Essas mudanças são necessárias para obter melhorias no React 19 e não quebrarão bibliotecas que seguem nossas diretrizes.

Com base em nossa Política de Versionamento, essas atualizações não estão listadas como breaking changes e não estamos incluindo documentos sobre como atualizá-las. A recomendação é remover qualquer código que dependa de internos.

Para refletir o impacto do uso de internos, renomeamos o sufixo SECRET_INTERNALS:

_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE

No futuro, bloquearemos com mais agressividade o acesso aos internos do React para desencorajar o uso e garantir que os usuários não sejam impedidos de fazer o upgrade.

Mudanças no TypeScript

Tipos TypeScript descontinuados removidos

Limpeza dos tipos TypeScript com base nas APIs removidas no React 19. Alguns dos removidos tiveram os tipos movidos para pacotes mais relevantes, e outros não são mais necessários para descrever o comportamento do React.

Note

Publicamos types-react-codemod para migrar a maioria das alterações de quebra relacionadas a tipos:

npx types-react-codemod@latest preset-19 ./path-to-app

Se você tiver muitos acessos não seguros a element.props, pode executar este codemod adicional:

npx types-react-codemod@latest react-element-default-any-props ./path-to-your-react-ts-files

Confira types-react-codemod para uma lista de substituições suportadas. Se você achar que um codemod está faltando, ele pode ser rastreado na lista de codemods ausentes do React 19.

Limpezas de ref necessárias

Essa mudança está incluída no preset de codemod react-19 como no-implicit-ref-callback-return.

Devido à introdução de funções de limpeza de ref, retornar qualquer outra coisa de um callback de ref agora será rejeitado pelo TypeScript. A correção geralmente é parar de usar retornos implícitos:

- <div ref={current => (instance = current)} />
+ <div ref={current => {instance = current}} />

O código original retornou a instância do HTMLDivElement e o TypeScript não saberia se isso deveria ser uma função de limpeza ou não.

useRef requer um argumento

Essa mudança está incluída no preset de codemod react-19 como refobject-defaults.

Uma reclamação de longa data de como TypeScript e React funcionam tem sido useRef. Alteramos os tipos para que useRef agora exija um argumento. Isso simplifica significativamente sua assinatura de tipo. Ele agora se comportará mais como createContext.

// @ts-expect-error: Esperava-se 1 argumento, mas nenhum foi visto
useRef();
// Passa
useRef(undefined);
// @ts-expect-error: Esperava-se 1 argumento, mas nenhum foi visto
createContext();
// Passa
createContext(undefined);

Isso agora também significa que todas as refs são mutáveis. Você não atingirá mais o problema em que não pode mutar uma ref porque a inicializou com null:

const ref = useRef<number>(null);

// Não é possível atribuir a 'current' porque é uma propriedade somente leitura
ref.current = 1;

MutableRef agora está descontinuado em favor de um único tipo RefObject que useRef sempre retornará:

interface RefObject<T> {
current: T
}

declare function useRef<T>: RefObject<T>

useRef ainda tem uma sobrecarga de conveniência para useRef<T>(null) que retorna automaticamente RefObject<T | null>. Para facilitar a migração devido ao argumento necessário para useRef, uma sobrecarga de conveniência para useRef(undefined) foi adicionada que retorna automaticamente RefObject<T | undefined>.

Confira [RFC] Make all refs mutable para discussões anteriores sobre essa mudança.

Mudanças no tipo TypeScript ReactElement

Essa mudança está incluída no codemod react-element-default-any-props.

As props dos elementos React agora são padronizadas para unknown em vez de any se o elemento for tipado como ReactElement. Isso não o afeta se você passar um argumento de tipo para ReactElement:

type Example2 = ReactElement<{ id: string }>["props"];
// ^? { id: string }

Mas se você confiava no padrão, agora precisa lidar com unknown:

type Example = ReactElement["props"];
// ^? Antes, era 'any', agora 'unknown'

Você só deve precisar disso se tiver muito código legado dependendo de acessos não seguros às props do elemento. A introspecção de elemento só existe como uma exceção e você deve deixar explícito que o acesso às suas props é insustentável por meio de um any explícito.

O namespace JSX no TypeScript

Essa alteração está incluída no preset react-19 codemod como scoped-jsx

Um pedido de longa data é remover o namespace global JSX de nossos tipos em favor de React.JSX. Isso ajuda a evitar a poluição de tipos globais, o que impede conflitos entre diferentes bibliotecas de UI que alavancam JSX.

Agora você precisará encapsular a ampliação do módulo do namespace JSX em `declare module ”…”:

// global.d.ts
+ declare module "react" {
namespace JSX {
interface IntrinsicElements {
"my-element": {
myElementProps: string;
};
}
}
+ }

O especificador de módulo exato depende do tempo de execução JSX que você especificou nas compilerOptions do seu tsconfig.json:

  • Para "jsx": "react-jsx" seria react/jsx-runtime.
  • Para "jsx": "react-jsxdev" seria react/jsx-dev-runtime.
  • Para "jsx": "react" e "jsx": "preserve" seria react.

Melhores tipagens useReducer

useReducer agora tem melhor inferência de tipos, graças a @mfp22.

No entanto, isso exigiu uma alteração de quebra onde useReducer não aceita o tipo de redutor completo como um parâmetro de tipo, mas em vez disso, precisa de nenhum (e depender da tipagem contextual) ou precisa tanto do estado quanto do tipo de ação.

A nova melhor prática é não passar argumentos de tipo para useReducer.

- useReducer<React.Reducer<State, Action>>(reducer)
+ useReducer(reducer)

Isso pode não funcionar em casos extremos em que você pode digitar explicitamente o estado e a ação, passando o Action em uma tupla:

- useReducer<React.Reducer<State, Action>>(reducer)
+ useReducer<State, [Action]>(reducer)

Se você definir o redutor embutido, incentivamos a anotar os parâmetros da função:

- useReducer<React.Reducer<State, Action>>((state, action) => state)
+ useReducer((state: State, action: Action) => state)

Isso também é o que você também teria que fazer se mover o redutor para fora da chamada useReducer:

const reducer = (state: State, action: Action) => state;

Changelog

Outras mudanças que quebram

  • react-dom: Error for javascript URLs in src and href #26507
  • react-dom: Remove errorInfo.digest from onRecoverableError #28222
  • react-dom: Remove unstable_flushControlled #26397
  • react-dom: Remove unstable_createEventHandle #28271
  • react-dom: Remove unstable_renderSubtreeIntoContainer #28271
  • react-dom: Remove unstable_runWithPriority #28271
  • react-is: Remove deprecated methods from react-is 28224

Outras mudanças notáveis

  • react: Batch sync, default and continuous lanes #25700
  • react: Don’t prerender siblings of suspended component #26380
  • react: Detect infinite update loops caused by render phase updates #26625
  • react-dom: Transitions in popstate are now synchronous #26025
  • react-dom: Remove layout effect warning during SSR #26395
  • react-dom: Warn and don’t set empty string for src/href (except anchor tags) #28124

Para uma lista completa de mudanças, consulte o Changelog.


Agradecimentos a Andrew Clark, Eli White, Jack Pope, Jan Kassens, Josh Story, Matt Carroll, Noah Lemen, Sophie Alpert, e Sebastian Silbermann por revisar e editar este post.