67204

How to detect the device on React SSR App with Next.js?

<h3>Question</h3>

on a web application I want to display two different Menu, one for the Mobile, one for the Desktop browser. I use Next.js application with server-side rendering and the library react-device-detect.

Here is the 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> This is rendered only in browser

</BrowserView> <MobileView>

This is rendered only on mobile

</MobileView> </div> );

If you open this in a browser and switch to mobile view and look the console you get this error:

<blockquote>

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

</blockquote>

This happen because the rendering by the server detects a browser and on the client, he is a mobile device. The only workaround I found is to generate both and use the CSS like this:

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

Instead of the library but I don't really like this method. Does someone know the good practice to handle devices type on an SSR app directly in the react code?


<h3>Answer1:</h3>

I think you should do it by using getInitialProps in your page, as it runs both on the server and on the client, and getting the device type by first detecting if you are just getting the request for the webpage (so you are still on the server), or if you are re-rendering (so you are on the client).

// index.js IndexPage.getInitialProps = ({ req }) => { let userAgent; if (req) { // if you are on the server and you get a 'req' property from your context userAgent = req.headers['user-agent'] // get the user-agent from the headers } else { userAgent = navigator.userAgent // if you are on the client you can access the navigator from the window object } }

Now you can use a regex to see if the device is a mobile or a desktop.

// still in getInitialProps let isMobile = Boolean(userAgent.match( /Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile|WPDesktop/i )) return { isMobile }

Now you can access the isMobile prop that will return either true or false

const IndexPage = ({ isMobile }) => { return ( <div> {isMobile ? (

I am on mobile!

) : (

I am on desktop!

)} </div> ) }

I got this answer from this article here I hope that was helpful to you


<h3>Answer2:</h3>

Personally I just use a hook to do this, although the initial props method is better.

<pre class="lang-js prettyprint-override">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

I had the problem that scroll animation was annoying on mobile devices so I made a device based enabled scroll animation component;

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} /> ) }


UPDATE:

so after further going down the rabbit hole, the best solution i came up with is using the react-device-detect in a useEffect, if you further inspect the device detect you will notice that it exports const's that are set via the ua-parser-js lib

<pre class="lang-js prettyprint-override">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);

This results in the initial device being the server which causes false detection.

I forked the repo and created and added a ssr-selector which requires you to pass in a user-agent. which could be done using the initial props


UPDATE:

Because of Ipads not giving a correct or rather well enough defined user-agent, see this issue, I decided to create a hook to better detect the device

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

Because I require the package each time I call that hook, the UA info is updated, it also fixes to SSR out of sync warnings.

来源:https://stackoverflow.com/questions/59494037/how-to-detect-the-device-on-react-ssr-app-with-next-js

Recommend

  • Browser detection using user agent in fixture hooks
  • What is the difference between webkit and moz
  • OpenFileDialog() locks the folder
  • how to set UIBarButtonItem selected or highlighted image or tint colours in iOS 7?
  • Function to accept variable number of chained calls like `add(1)(2)(3)(4)` [duplicate]
  • Detecting a mobile device with spring-boot
  • reshape a dataframe with tidyr or reshape2 [duplicate]
  • dynamic height of chart of highchart
  • javascript sidebar stay open on page change
  • Java threads, green or native?
  • Get All IP and Mac Address in lan
  • Order the result by best match on other field
  • Parse and remove special characters in java regex
  • React Warning: Can't call setState (or forceUpdate) on an unmounted component
  • Velocity (VM) template request parameters: Getting GET variables
  • How to read contents of a directory recursively in Linux Kernel?
  • Wrap array elements in divs based on same value
  • analytics.js event not working properly
  • Validating a Firebase Key [duplicate]
  • Windows: How do I get the mode/access rights of an already opened file?
  • Jenkins - could not read Username for 'https://github.com': No such device or address
  • Sample deviceQuery cuda program
  • Set initial vuetify v-select value
  • Run a form (insert/update/delete) from within a div using jquery
  • How to select multiple items from a List view - JavaFX 8
  • Wireshark Display Filter for Unique Source/Destination IP and Protocol
  • When to use the tag in the head and body section of a html page? [duplicate]
  • Annotate objects in a queryset with next and previous object ids
  • Neo4j…how to get a visual representation of my data?
  • Support of :after in IE7
  • Cloud Code: Creating a Parse.File from URL
  • How to encrypt Connectionstring written in web.config from codebehind?
  • Computing the discrete fourier transform of audio data with FFTW
  • XEP-0166: Jingle protocol implementation for voice/video chat in iOS
  • Sign a Pdf using custom digital signature in Java
  • Using Service Component Runtime
  • convert json to excel in java
  • Create/delete users from text file using Bash script