Repository: alibaba/hooks
Pull request #1862 (2022-09-05 07:49:25)

修复useFullScreen当全屏后，子元素重复全屏和退出全屏操作后父元素也会退出全屏
<!--
首先，感谢你的贡献！😄

新特性请提交至 master 分支。
在维护者审核通过后会合并。
请确保填写以下 pull request 的信息，谢谢！~
-->

[[English Template / 英文模板](https://github.com/alibaba/hooks/blob/master/.github/PULL_REQUEST_TEMPLATE.md)]

### 🤔 这个变动的性质是？

- [ ] 新特性提交
- [x] 日常 bug 修复
- [ ] 站点、文档改进
- [ ] 演示代码改进
- [ ] TypeScript 定义更新
- [ ] 包体积优化
- [ ] 性能优化
- [ ] 功能增强
- [ ] 国际化改进
- [ ] 重构
- [ ] 代码风格优化
- [ ] 测试用例
- [ ] 分支合并
- [ ] 其他改动（是关于什么的改动？）

### 🔗 相关 Issue

<!--
修复useFullScreen当全屏后，子元素重复全屏和退出全屏操作后父元素也会退出全屏
-->

### 💡 需求背景和解决方案

```tsx
  const onChange = () => {
    if (screenfull.isEnabled) {
      const el = getTargetElement(target);
      if (!el) {
        return;
      }
      const isFullscreen = document.fullscreenElement === el;
      if (isFullscreen) {
        onEnterRef.current?.();
      } else {
        screenfull.off('change', onChange);
        onExitRef.current?.();
      }
      setState(isFullscreen);
    }
  };

```

### 📝 更新日志

<!--
从用户角度描述具体变化，以及可能的 breaking change 和其他风险。
-->

| 语言    | 更新描述 |
| ------- | -------- |
| 🇺🇸 英文 |      Fix useFullScreen when full screen, the parent element will also exit full screen after the child element repeats the full screen and exits the full screen operation    |
| 🇨🇳 中文 |    修复useFullScreen当全屏后，子元素重复全屏和退出全屏操作后父元素也会退出全屏      |

### ☑️ 请求合并前的自查清单

⚠️ 请自检并全部**勾选全部选项**。⚠️

- [x] 文档已补充或无须补充
- [x] 代码演示已提供或无须提供
- [x] TypeScript 定义已补充或无须补充
- [x] Changelog 已提供或无须提供

brickspert: JS 只支持同时一个元素在全屏中，不存在父级和子级一起全屏的时候。

所以，没太理解为什么要加这个判断。

https://developer.mozilla.org/zh-CN/docs/Web/API/Fullscreen_API
Easy-Martin: > JS 只支持同时一个元素在全屏中，不存在父级和子级一起全屏的时候。
> 
> 所以，没太理解为什么要加这个判断。
> 
> https://developer.mozilla.org/zh-CN/docs/Web/API/Fullscreen_API

当通过脚本控制全屏时确实存在不一致的情况
```tsx
import { useFullscreen } from 'ahooks';
import React, { useRef } from 'react';

export default function App() {
  const ref1 = useRef();
  const ref2 = useRef();
  const [fullscreen1, options1] = useFullscreen(ref1);
  const [fullscreen2, options2] = useFullscreen(ref2);
  return (
    <div className="App">
      <div style={{ width: 400, height: 400, background: 'blue' }} ref={ref1}>
        <button onClick={options1.toggleFullscreen}>父元素{fullscreen1 ? '退出全屏' : '全屏'}</button>
        <div style={{ width: 200, height: 200, background: 'red' }} ref={ref2}>
          <button onClick={options2.toggleFullscreen}>子元素{fullscreen2 ? '退出全屏' : '全屏'}</button>
        </div>
      </div>
      <div></div>
    </div>
  );
}

```

当fullscreen1在全屏时，通过点击触发fullscreen2的全屏；然后再次点击触发fullscreen2退出全屏，此时fullscreen还是全屏；但是重复fullscreen2操作多次（测试2次）后fullscreen1也会退出全屏
brickspert: 目前 useFullScreen 对多个元素同时全屏支持不好，你的改法没改全，比如 exitFullscreen 也会导致父级退出，这个我们处理下。
Easy-Martin: > 目前 useFullScreen 对多个元素同时全屏支持不好，你的改法没改全，比如 exitFullscreen 也会导致父级退出，这个我们处理下。

ok

CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/alibaba/hooks?pullRequest=1862) <br/>Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our [Contributor License Agreement](https://cla-assistant.io/alibaba/hooks?pullRequest=1862) before we can accept your contribution.<br/>**1** out of **2** committers have signed the CLA.<br/><br/>:white_check_mark: Easy-Martin<br/>:x: miracles1919<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/alibaba/hooks?pullRequest=1862) it.</sub>

Patch:
diff --git a/packages/hooks/src/useAntdTable/index.tsx b/packages/hooks/src/useAntdTable/index.tsx
index 52a5adbe..8671fc82 100644
--- a/packages/hooks/src/useAntdTable/index.tsx
+++ b/packages/hooks/src/useAntdTable/index.tsx
@@ -43,7 +43,7 @@ const useAntdTable = <TData extends Data, TParams extends Params>(
   const isAntdV4 = !!form?.getInternalHooks;
 
   // get current active field values
-  const getActivetFieldValues = () => {
+  const getActiveFieldValues = () => {
     if (!form) {
       return {};
     }
@@ -69,7 +69,7 @@ const useAntdTable = <TData extends Data, TParams extends Params>(
     if (!form) {
       return Promise.resolve({});
     }
-    const activeFieldsValue = getActivetFieldValues();
+    const activeFieldsValue = getActiveFieldValues();
     const fields = Object.keys(activeFieldsValue);
 
     // antd 4
@@ -109,7 +109,7 @@ const useAntdTable = <TData extends Data, TParams extends Params>(
   };
 
   const changeType = () => {
-    const activeFieldsValue = getActivetFieldValues();
+    const activeFieldsValue = getActiveFieldValues();
     allFormDataRef.current = {
       ...allFormDataRef.current,
       ...activeFieldsValue,
diff --git a/packages/hooks/src/useFullscreen/index.ts b/packages/hooks/src/useFullscreen/index.ts
index bf2f3904..43a76974 100644
--- a/packages/hooks/src/useFullscreen/index.ts
+++ b/packages/hooks/src/useFullscreen/index.ts
@@ -21,14 +21,21 @@ const useFullscreen = (target: BasicTarget, options?: Options) => {
 
   const onChange = () => {
     if (screenfull.isEnabled) {
-      const { isFullscreen } = screenfull;
-      if (isFullscreen) {
-        onEnterRef.current?.();
-      } else {
-        screenfull.off('change', onChange);
+      const el = getTargetElement(target);
+
+      if (!screenfull.element) {
         onExitRef.current?.();
+        setState(false);
+        screenfull.off('change', onChange);
+      } else {
+        const isFullscreen = screenfull.element === el;
+        if (isFullscreen) {
+          onEnterRef.current?.();
+        } else {
+          onExitRef.current?.();
+        }
+        setState(isFullscreen);
       }
-      setState(isFullscreen);
     }
   };
 
@@ -49,7 +56,8 @@ const useFullscreen = (target: BasicTarget, options?: Options) => {
   };
 
   const exitFullscreen = () => {
-    if (screenfull.isEnabled) {
+    const el = getTargetElement(target);
+    if (screenfull.isEnabled && screenfull.element === el) {
       screenfull.exit();
     }
   };


Test patch:
diff --git a/packages/hooks/src/useFullscreen/__tests__/index.test.ts b/packages/hooks/src/useFullscreen/__tests__/index.test.ts
index afe5e689..ce67de4b 100644
--- a/packages/hooks/src/useFullscreen/__tests__/index.test.ts
+++ b/packages/hooks/src/useFullscreen/__tests__/index.test.ts
@@ -124,4 +124,55 @@ describe('useFullscreen', () => {
     unmount();
     expect(events.fullscreenchange.size).toBe(size - 1);
   });
+
+  it('`isFullscreen` should be false when use `document.exitFullscreen`', () => {
+    const { result } = setup(targetEl);
+    const { enterFullscreen } = result.current[1];
+    enterFullscreen();
+    act(() => {
+      events['fullscreenchange'].forEach((fn: any) => fn());
+    });
+    expect(result.current[0]).toBeTruthy();
+
+    document.exitFullscreen();
+    act(() => {
+      events['fullscreenchange'].forEach((fn: any) => fn());
+    });
+    expect(result.current[0]).toBeFalsy();
+  });
+
+  it('mutli element full screen should be correct', () => {
+    const targetEl2 = document.createElement('p');
+    const hook = setup(targetEl);
+    const hook2 = setup(targetEl2);
+
+    // target1 full screen
+    hook.result.current[1].enterFullscreen();
+    act(() => {
+      events['fullscreenchange'].forEach((fn: any) => fn());
+    });
+    expect(hook.result.current[0]).toBeTruthy();
+
+    // target2 full screen
+    hook2.result.current[1].enterFullscreen();
+    Object.defineProperty(document, 'fullscreenElement', {
+      value: targetEl2,
+    });
+    act(() => {
+      events['fullscreenchange'].forEach((fn: any) => fn());
+    });
+    expect(hook.result.current[0]).toBeFalsy();
+    expect(hook2.result.current[0]).toBeTruthy();
+
+    // target2 exit full screen
+    hook2.result.current[1].exitFullscreen();
+    Object.defineProperty(document, 'fullscreenElement', {
+      value: targetEl,
+    });
+    act(() => {
+      events['fullscreenchange'].forEach((fn: any) => fn());
+    });
+    expect(hook.result.current[0]).toBeTruthy();
+    expect(hook2.result.current[0]).toBeFalsy();
+  });
 });