From e59b09c3d06616f7764a345277dfef43b57f98e1 Mon Sep 17 00:00:00 2001 From: MadCcc <1075746765@qq.com> Date: Mon, 20 Feb 2023 17:51:23 +0800 Subject: [PATCH] feat: useMobile hook (#422) * feat: useMobile hook * feat: use useLayoutEffect * chore: update comment --- .husky/pre-commit | 4 ++++ package.json | 11 ++++++++++- src/hooks/useMobile.ts | 18 ++++++++++++++++++ tests/hooks.test.js | 27 ++++++++++++++++++++++++--- 4 files changed, 56 insertions(+), 4 deletions(-) create mode 100755 .husky/pre-commit create mode 100644 src/hooks/useMobile.ts diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 00000000..7d0de5da --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +lint-staged diff --git a/package.json b/package.json index c7107f95..f2d5f5ab 100644 --- a/package.json +++ b/package.json @@ -20,14 +20,21 @@ "es" ], "scripts": { + "build": "dumi build", "compile": "father build", "coverage": "npm test -- --coverage", - "build": "dumi build", "lint": "eslint src/ --ext .tsx,.ts & eslint tests/ --ext .js", + "prepare": "husky install", "prepublishOnly": "npm run compile && np --yolo --no-publish", "start": "dumi dev", "test": "rc-test" }, + "lint-staged": { + "**/*.{js,jsx,tsx,ts,md,json}": [ + "prettier --write", + "git add" + ] + }, "dependencies": { "@babel/runtime": "^7.18.3", "react-is": "^16.12.0" @@ -47,6 +54,8 @@ "eslint": "~7.32.0", "father": "^4.1.3", "glob": "^7.1.6", + "husky": "^8.0.3", + "lint-staged": "^13.1.2", "np": "^6.2.3", "rc-test": "^7.0.14", "react": "^18.0.0", diff --git a/src/hooks/useMobile.ts b/src/hooks/useMobile.ts new file mode 100644 index 00000000..af088744 --- /dev/null +++ b/src/hooks/useMobile.ts @@ -0,0 +1,18 @@ +import { useLayoutEffect, useState } from 'react'; +import isMobile from '../isMobile'; + +/** + * Hook to detect if the user is on a mobile device + * Notice that this hook will only detect the device type in effect, so it will always be false in server side + */ +const useMobile = (): boolean => { + const [mobile, setMobile] = useState(false); + + useLayoutEffect(() => { + setMobile(isMobile()); + }, []); + + return mobile; +}; + +export default useMobile; diff --git a/tests/hooks.test.js b/tests/hooks.test.js index c911bb0e..32fd4357 100644 --- a/tests/hooks.test.js +++ b/tests/hooks.test.js @@ -1,11 +1,12 @@ +import { fireEvent, render } from '@testing-library/react'; import * as React from 'react'; import { renderToString } from 'react-dom/server'; -import { render, fireEvent } from '@testing-library/react'; +import useId, { resetUuid } from '../src/hooks/useId'; +import useLayoutEffect from '../src/hooks/useLayoutEffect'; import useMemo from '../src/hooks/useMemo'; import useMergedState from '../src/hooks/useMergedState'; -import useLayoutEffect from '../src/hooks/useLayoutEffect'; +import useMobile from '../src/hooks/useMobile'; import useState from '../src/hooks/useState'; -import useId, { resetUuid } from '../src/hooks/useId'; global.disableUseId = false; @@ -498,4 +499,24 @@ describe('hooks', () => { global.disableUseId = false; }); }); + + describe('useMobile', () => { + it('should work', () => { + const Demo = () => { + const isMobile = useMobile(); + return
{isMobile ? 'mobile' : 'pc'}
; + }; + + const { container } = render(); + expect(container.textContent).toBe('pc'); + + const navigatorSpy = jest + .spyOn(navigator, 'userAgent', 'get') + .mockImplementation(() => 'Android'); + const { container: container2 } = render(); + expect(container2.textContent).toBe('mobile'); + + navigatorSpy.mockRestore(); + }); + }); });