6 votes

Erreurs d'hydratation de NextJS (Le contenu du texte ne correspond pas au HTML rendu par le serveur).

J'ai deux erreurs uniquement lorsque l'application est déployée, pas en mode dev ni en build local, ce qui est un tout autre problème.

1ère erreur : Le contenu du texte ne correspond pas au HTML rendu par le serveur.

2ème erreur : Il y a eu une erreur pendant l'hydratation. Comme l'erreur s'est produite à l'extérieur d'une zone de suspense, la racine entière va passer au rendu client.

J'ai découvert que les erreurs sont causées par le formatage de l'heure que je reçois d'une API au format timestamp Unix (current.dt), mais je ne sais pas comment gérer cela autrement. Devrais-je formater la date sur le serveur, c'est-à-dire dans getServerSideProps ? Ou ces erreurs sont-elles dues à autre chose ?

Editar : Application en direct avec des erreurs : https://weather-test-mu.vercel.app/

Affichage du temps :

import type { Current, Location } from '../../types/typeWeatherApi';

const WeatherDisplay = ({
    location,
    current,
}: {
    location: Location;
    current: Current;
}) => {
    // This causes the errors:
    const hour = ('0' + new Date(current.dt * 1000).getHours()).slice(-2);
    const minutes = ('0' + new Date(current.dt * 1000).getMinutes()).slice(-2);
    const currentTime = `${hour}:${minutes}`;

    return (
        <article>
            <h1>{location.name}</h1>
            <p>{location.country}</p>
            <p>{current.feels_like}</p>
            {/* This causes the error: */}
            <p>{currentTime}</p>
        </article>
    );
};

export default WeatherDisplay;

Page d'index avec getServerSideProps :

import axios from 'axios';
import { useState } from 'react';

import type { NextPage, GetServerSideProps } from 'next';
import type { Data, Location } from '../types/typeWeatherApi';

import { getCurrentWeather } from './api/currentWeather';
import { getCurrentLocation } from './api/currentLocation';

import WeatherDisplay from '../components/weather-display/weather-display';

const Home: NextPage = ({ initialWeather, initialLocation }: any) => {
    const [location, setLocation] = useState<Location>(initialLocation);
    const [weatherData, setWeatherData] = useState<Data>(initialWeather);
    const [units, setUnits] = useState('metric');
    const [lang, setLang] = useState('en');

    const getGeolocationData = () => {
        navigator.geolocation.getCurrentPosition(
            (position) => {
                axios
                    .get(
                        `/api/currentLocation?lon=${position.coords.longitude}&lat=${position.coords.latitude}`
                    )
                    .then((response) => setLocation(response.data[0]));
            },
            (error) => {
                console.warn(`ERROR(${error.code}): ${error.message}`);
            },
            {
                timeout: 10000,
                maximumAge: 0,
            }
        );
    };

    const getCurrentWeather = async () => {
        await axios
            .get(
                `/api/currentWeather?lon=${location.lon}&lat=${location.lat}&units=${units}&lang=${lang}`
            )
            .then((response) => setWeatherData(response.data))
            .catch((error) => console.error(error));
    };

    return (
        <>
            <section>
                <div>
                    <p>Latitude: {location.lat}</p>
                    <p>Longitude: {location.lon}</p>
                </div>
                <button onClick={getGeolocationData}>Get Current Location</button>

                <button onClick={getCurrentWeather}>Get Current Weather</button>
            </section>
            <section className="current-weather">
                <WeatherDisplay location={location} current={weatherData.current} />
            </section>
        </>
    );
};

export const getServerSideProps: GetServerSideProps = async () => {
    const defaultWeatherQuery = {
        lat: '51.5072',
        lon: '0.1276',
        exclude: '',
        units: 'metric',
        lang: 'en',
    };

    const defaultLocationQuery = {
        lat: defaultWeatherQuery.lat,
        lon: defaultWeatherQuery.lon,
    };

    const defaultWeather = await getCurrentWeather(defaultWeatherQuery);
    const defaultLocation = await getCurrentLocation(defaultLocationQuery);

    return {
        props: {
            initialWeather: defaultWeather,
            initialLocation: defaultLocation[0],
        },
    };
};

export default Home;

3voto

Karel Points 51

J'ai reçu une réponse de Discussions sur GitHub sur next.js, je vais le coller ici pour toute personne rencontrant le même problème que moi :

Citation icyJoseph :

Salut,

Oui, donc ces deux erreurs sont combinées.

Parce que le client, sur la première trame, doit voir le même HTML que que le serveur a envoyé, afin de placer des écouteurs d'événements et de placer les frères et les enfants correctement, s'il y a une erreur pendant que cela est pendant que ça se passe, React enregistre une erreur. Cela s'est produit jusqu'à React 17 AFAIK.

Le problème numéro 2 se pose avec le nouveau rendu Root, qui voit ceci comme une erreur de rendu, ce qui est défavorable aux fonctionnalités concurrentes, donc l'hydratation échoue et il jette l'ensemble par la fenêtre.

Du moins, c'est ainsi que j'interprète la deuxième erreur. Beaucoup de gens ont juste ont ignoré l'erreur numéro 1, pendant les 2 années où React 17 est sorti, je ne dis pas que vous l'avez fait, mais beaucoup utilisent des bibliothèques qui l'ont fait, et d'autres ont juste les ont ignorées. Contournement possible

Le temps et le hasard sont deux des choses qui produisent le plus souvent ceci.

J'attaquerais ce problème de la manière suivante :

  1. Laissez le serveur afficher l'heure, mais au format UTC.
  2. Si vous le souhaitez, mettez une feuille de style en cascade (CSS) qui permet de masquer l'élément, mais qui reste présent dans la mise en page (et non d'afficher none)
  3. Depuis le client, mettez à jour le fuseau horaire correct et rendez l'heure visible.

    import { CSSProperties, useEffect, useState } from "react"; import { Example } from "../components/Example";

    const TimeDisplay = ({ time }: { time: number }) => { const [currentTime, setCurrentTime] = useState(() => { const hour = ("0" + new Date(time 1000).getUTCHours()).slice(-2); const minutes = ("0" + new Date(time 1000).getUTCMinutes()).slice(-2);

    return `${hour}:${minutes} UTC`;

    });

    useEffect(() => { setCurrentTime(() => { const hour = ("0" + new Date(time 1000).getHours()).slice(-2); const minutes = ("0" + new Date(time 1000).getMinutes()).slice(-2);

      return `${hour}:${minutes}`;
    });

    }, [time]);

    // optionally make this content take space, but remain invisible, to avoid layout shifts // it's better to use a CSS class instead const style: CSSProperties = { visibility: currentTime.includes("UTC") ? "hidden" : "visible", };

    return ( <article> <p style={style}>{currentTime}</p> </article> ); };

Je vous recommande également d'intégrer l'API DateTimeFormat, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat , qui peut se charger d'afficher le fuseau horaire pour vous :

const date = new Date(Date.UTC(2012, 11, 20, 3, 0, 0, 200));

options = {
  hour: 'numeric', minute: 'numeric', second: 'numeric', 
  timeZoneName: 'short' 
};

console.log(new Intl.DateTimeFormat('sv-SE', options).format(date));

Si j'exécute ce qui précède sur un repl j'obtiens 3:00:00 AM UTC (ce qui devrait être ce que votre serveur envoie), mais si je l'exécute sur les navigateurs j'obtiens, 4:00:00 CET.

Le code source de votre page contient le temps vu par le serveur.

Comme les utilisateurs arrivent chez vous depuis différents fuseaux horaires, vous devez tenir compte d'un cadre où vous affichez l'UTC, puis l'heure du fuseau horaire de l'utilisateur. le fuseau horaire de l'utilisateur. Combiner cela avec la visibilité CSS est une bonne chose, parce que vous pouvez éviter les décalages de mise en page et autres, l'heure prendra sa place, mais elle ne sera pas visible.

Les robots, les crawlers voient toujours l'heure, et pour les utilisateurs avec JS désactivé, vous pouvez ajouter une balise qui rend la date à nouveau visible.

0voto

ibrahim Points 1

Étapes pour reproduire le bogue ou le problème créer l'application next installer next-ui ajouter CssBaseline.flush() à _document.js

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X