Cookies
This website uses cookies to improve the user experience, more information on the legal information path.
Notas
0
anos
0
meses
0
días
0
horas
0
minus
0
segus
00
Esta é a imaxe de fondo
6

Como arranxar Minified React error #425 (erro de hidratación)

Resposta rápida

O erro #425 de React —"Text content does not match server-rendered HTML"— é un erro de hidratación: o servidor renderizou un valor e o navegador outro distinto, así que React descarta o HTML do servidor para ese subárbore. O arranxo depende de por que difiren:

  • O valor difire de forma lexítima (unha data, unha hora) → engade suppressHydrationWarning (Arranxo 1).
  • O valor só existe no navegador (window, localStorage) → renderízao tras a montaxe (Arranxo 2).
  • O compoñente enteiro é só-cliente → cárgao con next/dynamic e ssr: false (Arranxo 3).
  • O teu HTML é inválido (un <div> dentro dun <p>) → corrixe o aniñamento (Arranxo 4).

O erro completo en produción é: Uncaught Error: Minified React error visit https://reactjs.org/docs/error-decoder.html?invariant=425 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.

Que significa realmente "Minified React error #425"

Nunha build de produción React envía as mensaxes de erro minificadas para manter o bundle pequeno, así que en vez dunha frase recibes un número e unha ligazón descodificadora (invariant=425). O #425 descodifica a "Text content does not match server-rendered HTML".

Iso só ocorre con renderizado no servidor (Next.js, Remix, renderToString): React renderiza o HTML unha vez no servidor, envíao ao navegador e logo hidrata —reutiliza ese HTML e só engancha os manexadores de eventos, agardando que o primeiro render do cliente produza exactamente a mesma saída—. Cando un anaco de texto sae distinto, a hidratación dese subárbore falla e aparece o #425.

Como a mensaxe está minificada, o paso de depuración máis útil é reproducilo nunha build de desenvolvemento: aí React imprime a mensaxe completa e sinala o elemento exacto, o que converte as conxecturas nun arranxo dunha liña (ver "Como ver o erro real" máis abaixo).

Por que ocorren os erros de hidratación

Todo #425 redúcese a saída do servidor ≠ primeira saída do cliente. Os sospeitosos habituais:

| Causa | Por que difire | | ----------------------------------------- | -------------------------------------------------------------------- | | new Date() / toLocaleString | O reloxo/zona do servidor ≠ o reloxo/zona do navegador | | Math.random(), Date.now(), uuid() | Un valor novo en cada render, servidor e cliente nunca coinciden | | Formato de número/moeda por locale | O locale do servidor ≠ o do navegador do usuario | | window, localStorage, navigator | Indefinidos no servidor, presentes no navegador | | Aniñamento HTML inválido | O navegador "arranxa" o teu HTML, así que o DOM ≠ o que renderizou React | | Extensións do navegador | Modifican o DOM antes de que React hidrate |

Diagrama dun erro de hidratación: o servidor renderiza a hora como 14:32, o navegador vólvea renderizar como 14:33 ao hidratar, o texto non coincide e React lanza o erro #425.

O exemplo de abaixo é o caso clásico: un reloxo en vivo.

O código que provocaba o erro

DateAndHour.tsx

const DateAndHour = () => {
const { locale } = useRouter();
const [date, setDate] = React.useState(new Date());
const day = date.toLocaleDateString(locale, { weekday: 'short' });
const dayNumber = date.toLocaleDateString(locale, { day: 'numeric' });
const month = date.toLocaleDateString(locale, { month: 'short' });
const hour = date.toLocaleTimeString(locale, { hour: 'numeric', minute: 'numeric' });
React.useEffect(() => {
const interval = setInterval(() => {
setDate(new Date());
}, 60000);
return () => clearInterval(interval);
}, []);
return (
<div className={styles.dateAndHour}>
<span>{day}</span>
<span>{dayNumber}</span>
<span>{month}</span>
<span>{hour}</span>
</div>
);
};

O servidor renderiza a hora para o seu reloxo e zona horaria; un segundo despois o navegador renderiza a súa —o texto difire e React lanza o #425—.

Arranxo 1 — suppressHydrationWarning para diferenzas de texto lexítimas

Cando a diferenza é real e agardada (un reloxo, un "hai 2 minutos"), dille a React que a acepte nese elemento con suppressHydrationWarning. Só silencia unha diferenza de texto un nivel de profundidade: é un bisturí, non unha manta, así que non o espalles por todas partes.

DateAndHour.tsx

const DateAndHour = () => {
const { locale } = useRouter();
const [date, setDate] = React.useState(new Date());
const day = date.toLocaleDateString(locale, { weekday: 'short' });
const dayNumber = date.toLocaleDateString(locale, { day: 'numeric' });
const month = date.toLocaleDateString(locale, { month: 'short' });
const hour = date.toLocaleTimeString(locale, { hour: 'numeric', minute: 'numeric' });
React.useEffect(() => {
const interval = setInterval(() => setDate(new Date()), 60000);
return () => clearInterval(interval);
}, []);
return (
<div className={styles.dateAndHour}>
<span suppressHydrationWarning>{day}</span>
<span suppressHydrationWarning>{dayNumber}</span>
<span suppressHydrationWarning>{month}</span>
<span suppressHydrationWarning>{hour}</span>
</div>
);
};

Arranxo 2 — Renderiza os valores do navegador tras a montaxe

Se o valor sinxelamente non existe no servidor (window, localStorage, matchMedia), renderiza primeiro un marcador estable e substitúeo polo valor real despois de que o compoñente se monte. O servidor e o primeiro render do cliente xa coinciden, e o valor do navegador aparece no seguinte pintado.

useMounted.tsx

// Renderiza o mesmo no servidor e no primeiro render do cliente,
// e revela o valor só-navegador unha vez que sabemos que estamos no cliente.
const useMounted = () => {
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => setMounted(true), []);
return mounted;
};
const Theme = () => {
const mounted = useMounted();
if (!mounted) return null; // mesma saída no servidor e primeiro render do cliente
return <span>{localStorage.getItem('theme')}</span>;
};

Arranxo 3 — Carga os compoñentes só-cliente con next/dynamic

Cando un compoñente enteiro non pode renderizarse nunca no servidor (unha gráfica que le window, un mapa, un editor), sáltate o SSR por completo en vez de protexer cada liña:

dynamic.tsx

import dynamic from 'next/dynamic';
const ClientOnlyChart = dynamic(() => import('./Chart'), { ssr: false });

Arranxo 4 — Corrixe o aniñamento HTML inválido

Este sorprende porque non hai ningunha data nin valor aleatorio á vista. Se aniñas un elemento de bloque dentro dun <p> (ou un <a> dentro dun <a>, ou un <div> dentro dunha <table> sen <tbody>), o navegador repara o HTML en silencio ao parsealo. React hidrata entón contra un DOM que xa non coincide co que renderizou, e aparece o #425. Non hai atributo que silencie isto: fai que o HTML sexa válido (un <p> só pode conter contido en liña).

Como ver o erro real

Nunca depures o #425 desde o número minificado. Reprodúceo onde React si che fala:

  • Next.js: executa next dev —o desenvolvemento nunca está minificado—.
  • Build de produción en local: next build && next start, pero le a mensaxe na consola do navegador, onde React imprime o texto completo e un diff do elemento que non cadra.

React rexistra algo como "Warning: Text content did not match. Server: '14:32' Client: '14:33'", sinalando o nodo exacto. A partir de aí a causa adoita ser obvia.

#418, #422, #423, #425 — a familia de erros de hidratación

Se as túas procuras trouxeron números veciños, son o mesmo problema visto desde ángulos distintos:

| Erro | Significado | | ----- | -------------------------------------------------------- | | #418 | A hidratación fallou porque a UI inicial difería | | #422 | A hidratación rematou pero contiña discrepancias | | #423 | Erro ao hidratar; a árbore caeu a renderizado en cliente | | #425 | O contido de texto non coincide co HTML do servidor |

O arranxo é sempre a mesma disciplina: executa unha build de desenvolvemento, atopa o valor que difire entre servidor e cliente, e reconcíliao cun dos catro arranxos de arriba.

Podes ler máis sobre o atributo na documentación de React.