Kar*_*rel 5 javascript typescript reactjs next.js
仅在部署应用程序时出现两个错误,而不是在开发模式或本地构建中,这完全是另一个问题。
第一个错误:文本内容与服务器呈现的 HTML 不匹配。
第二个错误:补水时出现错误。由于错误发生在 Suspense 边界之外,因此整个根将切换到客户端渲染。
我发现这些错误是由我从 Unix 时间戳格式 (current.dt) 的 API 获取的时间格式引起的,但我不知道如何处理此问题。我应该在服务器上格式化日期,即在 getServerSideProps 中吗?或者这些错误是由其他原因引起的?
编辑:有错误的实时应用程序:https ://weather-test-mu.vercel.app/
天气显示:
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;
Run Code Online (Sandbox Code Playgroud)
带有 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;
Run Code Online (Sandbox Code Playgroud)
我从GitHub 上关于 next.js 的讨论中得到了答案,我将把它粘贴到这里,供任何与我面临同样问题的人使用:
引用冰冷的约瑟夫:
你好,
是的,这两个错误结合在一起了。
因为客户端在第一帧上需要看到与服务器发送过来的相同的 HTML,以便放置事件侦听器并正确放置同级和子级,如果在执行此操作时出现错误,React 会记录错误。据我所知,这种情况早在 React 17 就已经发生过。
第二个问题是由新的渲染根开始的,它将此视为渲染错误,这对并发功能不友好,因此水合作用失败并将整个事情抛到了窗外。
至少我是这样解释第二个错误的。在 React 17 发布的整整 2 年里,很多人都忽略了第 1 号错误,并不是说你忽略了,但很多人使用了这样做的库,而其他人只是忽略了它们。可能的解决方法
时间和随机性是最常产生这种情况的两个因素。
我会这样解决这个问题:
- 让服务器呈现时间,但采用 UTC 格式
- (可选)放置 CSS 使其隐藏,但仍显示在布局上(不显示任何内容)
- 从客户端,更新到正确的时区,也使时间可见
Run Code Online (Sandbox Code Playgroud)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> ); };我建议还混合使用 DateTimeFormat API, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat,它可以为您显示时区:
Run Code Online (Sandbox Code Playgroud)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));如果我在repl上运行上面的代码,我会得到 3:00:00 AM UTC (这将是您的服务器发送的内容),但如果我在浏览器上运行它,我会得到 4:00:00 CET。
您页面的源代码包含服务器看到的时间。
由于用户来自不同的时区,因此您必须考虑显示 UTC 的框架,然后显示用户时区的时间。将其与 CSS 可见性相结合是很好的,因为您可以避免布局变化等,时间将取代它,但它不会是可见的。
机器人、爬虫仍然可以看到时间,对于禁用 JS 的用户,您可以添加一个标签,使日期再次可见。
| 归档时间: |
|
| 查看次数: |
7165 次 |
| 最近记录: |