diff --git a/packages/hooks/src/useFullscreen/__tests__/index.test.ts b/packages/hooks/src/useFullscreen/__tests__/index.test.ts index 96345e25a3..c453317175 100644 --- a/packages/hooks/src/useFullscreen/__tests__/index.test.ts +++ b/packages/hooks/src/useFullscreen/__tests__/index.test.ts @@ -3,17 +3,20 @@ import useFullscreen from '../index'; import type { Options } from '../index'; import type { BasicTarget } from '../../utils/domTarget'; -const targetEl = document.createElement('div'); +let globalHook: any; +let targetEl: any; const events = { fullscreenchange: new Set(), fullscreenerror: new Set(), }; - -const setup = (target: BasicTarget, options?: Options) => - renderHook(() => useFullscreen(target, options)); +const setup = (target: BasicTarget, options?: Options) => { + globalHook = renderHook(() => useFullscreen(target, options)); + return globalHook; +}; describe('useFullscreen', () => { beforeEach(() => { + targetEl = document.createElement('div'); document.body.appendChild(targetEl); jest.spyOn(HTMLElement.prototype, 'requestFullscreen').mockImplementation(() => { Object.defineProperty(document, 'fullscreenElement', { @@ -42,6 +45,7 @@ describe('useFullscreen', () => { afterEach(() => { document.body.removeChild(targetEl); events.fullscreenchange.clear(); + globalHook?.unmount(); }); afterAll(() => { @@ -163,10 +167,11 @@ describe('useFullscreen', () => { }); it('enterFullscreen should not work when target is not element', () => { - const { result } = setup(null); + const onEnter = jest.fn(); + const { result } = setup(null, { onEnter }); const { enterFullscreen } = result.current[1]; enterFullscreen(); - expect(events.fullscreenchange.size).toBe(0); + expect(onEnter).not.toBeCalled(); }); it('should remove event listener when unmount', () => { diff --git a/packages/hooks/src/useFullscreen/demo/demo4.tsx b/packages/hooks/src/useFullscreen/demo/demo4.tsx new file mode 100644 index 0000000000..bdcb12884a --- /dev/null +++ b/packages/hooks/src/useFullscreen/demo/demo4.tsx @@ -0,0 +1,39 @@ +/** + * title: Coexist with other full screen operations + * desc: The element's full screen may be modified by other scripts, don't worry, ahooks can work with them. + * + * title.zh-CN: 与其它全屏操作共存 + * desc.zh-CN: 元素的全屏情况可能被其它脚本修改,不用担心,ahooks 可以与它们共存。 + */ + +import React, { useRef } from 'react'; +import { useFullscreen } from 'ahooks'; + +function vanillaToggleFullscreen(element) { + const isFullscreen = !!document.fullscreenElement; + + if (isFullscreen) { + document.exitFullscreen(); + } else { + element.requestFullscreen(); + } +} + +export default () => { + const ref = useRef(null); + const [isFullscreen, { toggleFullscreen }] = useFullscreen(ref); + + return ( +
+
{isFullscreen ? 'Fullscreen' : 'Not fullscreen'}
+
+ + +
+
+ ); +}; diff --git a/packages/hooks/src/useFullscreen/index.en-US.md b/packages/hooks/src/useFullscreen/index.en-US.md index da8b251720..b7d03bc517 100644 --- a/packages/hooks/src/useFullscreen/index.en-US.md +++ b/packages/hooks/src/useFullscreen/index.en-US.md @@ -21,6 +21,10 @@ manages DOM full screen. +### Coexist with other full screen operations + + + ## API ```typescript diff --git a/packages/hooks/src/useFullscreen/index.ts b/packages/hooks/src/useFullscreen/index.ts index 942a7998bf..3220ca8fb1 100644 --- a/packages/hooks/src/useFullscreen/index.ts +++ b/packages/hooks/src/useFullscreen/index.ts @@ -1,8 +1,7 @@ -import { useState } from 'react'; +import { useEffect, useState, useRef } from 'react'; import screenfull from 'screenfull'; import useLatest from '../useLatest'; import useMemoizedFn from '../useMemoizedFn'; -import useUnmount from '../useUnmount'; import type { BasicTarget } from '../utils/domTarget'; import { getTargetElement } from '../utils/domTarget'; import { isBoolean } from '../utils'; @@ -26,7 +25,18 @@ const useFullscreen = (target: BasicTarget, options?: Options) => { const onExitRef = useLatest(onExit); const onEnterRef = useLatest(onEnter); - const [state, setState] = useState(false); + // The state of full screen may be changed by other scripts/components, + // so the initial value needs to be computed dynamically. + const [state, setState] = useState(getIsFullscreen); + const stateRef = useRef(getIsFullscreen()); + + function getIsFullscreen() { + return ( + screenfull.isEnabled && + !!screenfull.element && + screenfull.element === getTargetElement(target) + ); + } const invokeCallback = (fullscreen: boolean) => { if (fullscreen) { @@ -36,23 +46,20 @@ const useFullscreen = (target: BasicTarget, options?: Options) => { } }; - // Memoized, otherwise it will be listened multiple times. - const onScreenfullChange = useMemoizedFn(() => { - if (screenfull.isEnabled) { - const el = getTargetElement(target); + const updateFullscreenState = (fullscreen: boolean) => { + // Prevent repeated calls when the state is not changed. + if (stateRef.current !== fullscreen) { + invokeCallback(fullscreen); + setState(fullscreen); + stateRef.current = fullscreen; + } + }; - if (!screenfull.element) { - invokeCallback(false); - setState(false); - screenfull.off('change', onScreenfullChange); - } else { - const isFullscreen = screenfull.element === el; + const onScreenfullChange = () => { + const fullscreen = getIsFullscreen(); - invokeCallback(isFullscreen); - setState(isFullscreen); - } - } - }); + updateFullscreenState(fullscreen); + }; const togglePageFullscreen = (fullscreen: boolean) => { const el = getTargetElement(target); @@ -84,11 +91,7 @@ const useFullscreen = (target: BasicTarget, options?: Options) => { } } - // Prevent repeated calls when the state is not changed. - if (state !== fullscreen) { - invokeCallback(fullscreen); - setState(fullscreen); - } + updateFullscreenState(fullscreen); }; const enterFullscreen = () => { @@ -104,7 +107,6 @@ const useFullscreen = (target: BasicTarget, options?: Options) => { if (screenfull.isEnabled) { try { screenfull.request(el); - screenfull.on('change', onScreenfullChange); } catch (error) { console.error(error); } @@ -134,11 +136,17 @@ const useFullscreen = (target: BasicTarget, options?: Options) => { } }; - useUnmount(() => { - if (screenfull.isEnabled && !pageFullscreen) { - screenfull.off('change', onScreenfullChange); + useEffect(() => { + if (!screenfull.isEnabled || pageFullscreen) { + return; } - }); + + screenfull.on('change', onScreenfullChange); + + return () => { + screenfull.off('change', onScreenfullChange); + }; + }, []); return [ state, diff --git a/packages/hooks/src/useFullscreen/index.zh-CN.md b/packages/hooks/src/useFullscreen/index.zh-CN.md index fae61633b1..503bd58007 100644 --- a/packages/hooks/src/useFullscreen/index.zh-CN.md +++ b/packages/hooks/src/useFullscreen/index.zh-CN.md @@ -21,6 +21,10 @@ nav: +### 与其它全屏操作共存 + + + ## API ```typescript