gpt4 book ai didi

reactjs - 如何使用 Next.js 检测 React SSR 应用程序上的设备?

转载 作者:行者123 更新时间:2023-12-03 14:11:02 26 4
gpt4 key购买 nike

在 Web 应用程序上,我想显示两个不同的菜单,一个用于移动设备,一个用于桌面浏览器。我使用带有服务器端渲染的 Next.js 应用程序和库 react-device-detect .

这是CodeSandox link .

import Link from "next/link";
import { BrowserView, MobileView } from "react-device-detect";

export default () => (
<div>
Hello World.{" "}
<Link href="/about">
<a>About</a>
</Link>
<BrowserView>
<h1> This is rendered only in browser </h1>
</BrowserView>
<MobileView>
<h1> This is rendered only on mobile </h1>
</MobileView>
</div>
);

如果您在浏览器中打开此文件并切换到移动 View 并查看控制台,您会收到此错误:

Warning: Text content did not match. Server: " This is rendered only in browser " Client: " This is rendered only on mobile "

发生这种情况是因为服务器的渲染检测到浏览器,而在客户端上,他是移动设备。我发现的唯一解决方法是生成两者并使用 CSS,如下所示:

.activeOnMobile {
@media screen and (min-width: 800px) {
display: none;
}
}

.activeOnDesktop {
@media screen and (max-width: 800px) {
display: none;
}
}

而不是库,但我不太喜欢这种方法。有人知道直接在 react 代码中处理 SSR 应用程序上的设备类型的良好做法吗?

最佳答案

最新更新:

因此,如果您不介意在客户端执行此操作,则可以按照下面一些人的建议使用动态导入。这适用于使用静态页面生成的用例。

我创建了一个组件,它将所有 react-device-detect 导出作为 props 传递(明智的做法是仅过滤掉所需的导出,因为这样就不会摇树)

// Device/Device.tsx

import { ReactNode } from 'react'
import * as rdd from 'react-device-detect'

interface DeviceProps {
children: (props: typeof rdd) => ReactNode
}
export default function Device(props: DeviceProps) {
return <div className="device-layout-component">{props.children(rdd)}</div>
}

// Device/index.ts

import dynamic from 'next/dynamic'

const Device = dynamic(() => import('./Device'), { ssr: false })

export default Device

然后当你想使用该组件时,你就可以这样做

const Example = () => {
return (
<Device>
{({ isMobile }) => {
if (isMobile) return <div>My Mobile View</div>
return <div>My Desktop View</div>
}}
</Device>
)
}
<小时/>

我个人只是使​​用一个钩子(Hook)来做到这一点,尽管最初的 props 方法更好。

import { useEffect } from 'react'

const getMobileDetect = (userAgent: NavigatorID['userAgent']) => {
const isAndroid = () => Boolean(userAgent.match(/Android/i))
const isIos = () => Boolean(userAgent.match(/iPhone|iPad|iPod/i))
const isOpera = () => Boolean(userAgent.match(/Opera Mini/i))
const isWindows = () => Boolean(userAgent.match(/IEMobile/i))
const isSSR = () => Boolean(userAgent.match(/SSR/i))
const isMobile = () => Boolean(isAndroid() || isIos() || isOpera() || isWindows())
const isDesktop = () => Boolean(!isMobile() && !isSSR())
return {
isMobile,
isDesktop,
isAndroid,
isIos,
isSSR,
}
}
const useMobileDetect = () => {
useEffect(() => {}, [])
const userAgent = typeof navigator === 'undefined' ? 'SSR' : navigator.userAgent
return getMobileDetect(userAgent)
}

export default useMobileDetect

我遇到的问题是滚动动画在移动设备上很烦人,因此我制作了一个基于设备的启用滚动动画组件;

import React, { ReactNode } from 'react'
import ScrollAnimation, { ScrollAnimationProps } from 'react-animate-on-scroll'
import useMobileDetect from 'src/utils/useMobileDetect'

interface DeviceScrollAnimation extends ScrollAnimationProps {
device: 'mobile' | 'desktop'
children: ReactNode
}

export default function DeviceScrollAnimation({ device, animateIn, animateOut, initiallyVisible, ...props }: DeviceScrollAnimation) {
const currentDevice = useMobileDetect()

const flag = device === 'mobile' ? currentDevice.isMobile() : device === 'desktop' ? currentDevice.isDesktop() : true

return (
<ScrollAnimation
animateIn={flag ? animateIn : 'none'}
animateOut={flag ? animateOut : 'none'}
initiallyVisible={flag ? initiallyVisible : true}
{...props}
/>
)
}

更新:

因此,在进一步深入研究之后,我想出的最佳解决方案是在 useEffect 中使用react-device-detect,如果您进一步检查设备检测,您会注意到它导出通过设置的 const ua-parser-js lib

export const UA = new UAParser();

export const browser = UA.getBrowser();
export const cpu = UA.getCPU();
export const device = UA.getDevice();
export const engine = UA.getEngine();
export const os = UA.getOS();
export const ua = UA.getUA();
export const setUA = (uaStr) => UA.setUA(uaStr);

这会导致初始设备成为服务器,从而导致错误检测。

我 fork 了存储库并创建并添加了 ssr-selector这需要您传入用户代理。这可以使用初始 Prop 来完成


更新:

由于 Ipad 没有提供正确的或定义得足够好的用户代理,请参阅此 issue ,我决定创建一个钩子(Hook)来更好地检测设备

import { useEffect, useState } from 'react'

function isTouchDevice() {
if (typeof window === 'undefined') return false
const prefixes = ' -webkit- -moz- -o- -ms- '.split(' ')
function mq(query) {
return typeof window !== 'undefined' && window.matchMedia(query).matches
}
// @ts-ignore
if ('ontouchstart' in window || (window?.DocumentTouch && document instanceof DocumentTouch)) return true
const query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join('') // include the 'heartz' - https://git.io/vznFH
return mq(query)
}

export default function useIsTouchDevice() {
const [isTouch, setIsTouch] = useState(false)
useEffect(() => {
const { isAndroid, isIPad13, isIPhone13, isWinPhone, isMobileSafari, isTablet } = require('react-device-detect')
setIsTouch(isTouch || isAndroid || isIPad13 || isIPhone13 || isWinPhone || isMobileSafari || isTablet || isTouchDevice())
}, [])

return isTouch

因为我每次调用该钩子(Hook)时都需要该包,所以 UA 信息会更新,它还会修复 SSR 不同步警告。

关于reactjs - 如何使用 Next.js 检测 React SSR 应用程序上的设备?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59494037/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com