From f2392736e3c2e5a40376bf830c0c4897a094d322 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?=
 <smith3816@gmail.com>
Date: Fri, 16 Dec 2022 15:37:53 +0800
Subject: [PATCH 1/4] chore: init

---
 src/Align.tsx          | 12 +++----
 tests/element.test.tsx |  5 +--
 tests/strict.test.tsx  | 77 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 84 insertions(+), 10 deletions(-)
 create mode 100644 tests/strict.test.tsx

diff --git a/src/Align.tsx b/src/Align.tsx
index d8b4048..53ecf7e 100644
--- a/src/Align.tsx
+++ b/src/Align.tsx
@@ -154,15 +154,11 @@ const Align: React.ForwardRefRenderFunction<RefAlign, AlignProps> = (
   }, [disabled]);
 
   // Listen for window resize
-  const winResizeRef = React.useRef<{ remove: Function }>(null);
   React.useEffect(() => {
     if (monitorWindowResize) {
-      if (!winResizeRef.current) {
-        winResizeRef.current = addEventListener(window, 'resize', forceAlign);
-      }
-    } else if (winResizeRef.current) {
-      winResizeRef.current.remove();
-      winResizeRef.current = null;
+       const cancelFn = addEventListener(window, 'resize', forceAlign);
+
+       return cancelFn;
     }
   }, [monitorWindowResize]);
 
@@ -171,7 +167,7 @@ const Align: React.ForwardRefRenderFunction<RefAlign, AlignProps> = (
     () => () => {
       resizeMonitor.current.cancel();
       sourceResizeMonitor.current.cancel();
-      if (winResizeRef.current) winResizeRef.current.remove();
+      // if (winResizeRef.current) winResizeRef.current.remove();
       cancelForceAlign();
     },
     [],
diff --git a/tests/element.test.tsx b/tests/element.test.tsx
index 742761c..86d68ab 100644
--- a/tests/element.test.tsx
+++ b/tests/element.test.tsx
@@ -1,7 +1,7 @@
 /* eslint-disable class-methods-use-this */
-import React from 'react';
-import { render } from '@testing-library/react';
+import { act, render } from '@testing-library/react';
 import { spyElementPrototype } from 'rc-util/lib/test/domHook';
+import React from 'react';
 import Align from '../src';
 
 describe('element align', () => {
@@ -16,6 +16,7 @@ describe('element align', () => {
   });
 
   afterEach(() => {
+    jest.clearAllTimers();
     jest.useRealTimers();
   });
 
diff --git a/tests/strict.test.tsx b/tests/strict.test.tsx
new file mode 100644
index 0000000..94d4ea1
--- /dev/null
+++ b/tests/strict.test.tsx
@@ -0,0 +1,77 @@
+/* eslint-disable class-methods-use-this */
+import { act, render } from '@testing-library/react';
+import { spyElementPrototype } from 'rc-util/lib/test/domHook';
+import React from 'react';
+import Align from '../src';
+
+(global as any).watchCnt = 0;
+
+jest.mock('../src/util', () => {
+  const originUtil = jest.requireActual('../src/util');
+
+  return {
+    ...originUtil,
+    monitorResize: (...args: any[]) => {
+      (global as any).watchCnt += 1;
+      console.log('mount', (global as any).watchCnt);
+      const cancelFn = originUtil.monitorResize(...args);
+
+      return () => {
+        (global as any).watchCnt -= 1;
+        console.log('unmount', (global as any).watchCnt);
+        cancelFn();
+      };
+    },
+  };
+});
+
+describe('element align', () => {
+  beforeAll(() => {
+    spyElementPrototype(HTMLElement, 'offsetParent', {
+      get: () => ({}),
+    });
+  });
+
+  beforeEach(() => {
+    jest.useFakeTimers();
+  });
+
+  afterEach(() => {
+    jest.clearAllTimers();
+    jest.useRealTimers();
+  });
+
+  it('StrictMode should keep resize observer', () => {
+    const Demo = () => {
+      const targetRef = React.useRef(null);
+
+      return (
+        <>
+          <div ref={targetRef} />
+          <Align target={() => targetRef.current} align={{ points: ['bc', 'tc'] }}>
+            <div
+              style={{
+                position: 'absolute',
+                width: 50,
+                height: 80,
+              }}
+            />
+          </Align>
+        </>
+      );
+    };
+
+    render(
+      <React.StrictMode>
+        <Demo />
+      </React.StrictMode>,
+    );
+
+    act(() => {
+      jest.runAllTimers();
+    });
+
+    console.log('====================================');
+  });
+});
+/* eslint-enable */

From a42f352f1b5be2259bc0b41b6983ca6bc6bd44b0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?=
 <smith3816@gmail.com>
Date: Fri, 16 Dec 2022 16:25:44 +0800
Subject: [PATCH 2/4] chore: fin refactor

---
 src/Align.tsx | 99 +++++++++++++++++++++++++++++++++------------------
 1 file changed, 64 insertions(+), 35 deletions(-)

diff --git a/src/Align.tsx b/src/Align.tsx
index 53ecf7e..07bbb63 100644
--- a/src/Align.tsx
+++ b/src/Align.tsx
@@ -3,16 +3,16 @@
  *  - childrenProps
  */
 
-import React from 'react';
-import { composeRef } from 'rc-util/lib/ref';
-import isVisible from 'rc-util/lib/Dom/isVisible';
 import { alignElement, alignPoint } from 'dom-align';
-import addEventListener from 'rc-util/lib/Dom/addEventListener';
 import isEqual from 'lodash/isEqual';
+import addEventListener from 'rc-util/lib/Dom/addEventListener';
+import isVisible from 'rc-util/lib/Dom/isVisible';
+import { composeRef } from 'rc-util/lib/ref';
+import React from 'react';
 
-import { isSamePoint, restoreFocus, monitorResize } from './util';
-import type { AlignType, AlignResult, TargetType, TargetPoint } from './interface';
 import useBuffer from './hooks/useBuffer';
+import type { AlignResult, AlignType, TargetPoint, TargetType } from './interface';
+import { isSamePoint, monitorResize, restoreFocus } from './util';
 
 type OnAlign = (source: HTMLElement, result: AlignResult) => void;
 
@@ -26,10 +26,10 @@ export interface AlignProps {
   children: React.ReactElement;
 }
 
-interface MonitorRef {
-  element?: HTMLElement;
-  cancel: () => void;
-}
+// interface MonitorRef {
+//   element?: HTMLElement;
+//   cancel: () => void;
+// }
 
 export interface RefAlign {
   forceAlign: () => void;
@@ -52,6 +52,8 @@ const Align: React.ForwardRefRenderFunction<RefAlign, AlignProps> = (
   const cacheRef = React.useRef<{ element?: HTMLElement; point?: TargetPoint; align?: AlignType }>(
     {},
   );
+
+  /** Popup node ref */
   const nodeRef = React.useRef();
   let childNode = React.Children.only(children);
 
@@ -111,39 +113,66 @@ const Align: React.ForwardRefRenderFunction<RefAlign, AlignProps> = (
 
   // ===================== Effect =====================
   // Listen for target updated
-  const resizeMonitor = React.useRef<MonitorRef>({
-    cancel: () => {},
-  });
+  // const resizeMonitor = React.useRef<MonitorRef>({
+  //   cancel: () => {},
+  // });
   // Listen for source updated
-  const sourceResizeMonitor = React.useRef<MonitorRef>({
-    cancel: () => {},
-  });
-  React.useEffect(() => {
-    const element = getElement(target);
-    const point = getPoint(target);
+  // const sourceResizeMonitor = React.useRef<MonitorRef>({
+  //   cancel: () => {},
+  // });
 
-    if (nodeRef.current !== sourceResizeMonitor.current.element) {
-      sourceResizeMonitor.current.cancel();
-      sourceResizeMonitor.current.element = nodeRef.current;
-      sourceResizeMonitor.current.cancel = monitorResize(nodeRef.current, forceAlign);
-    }
+  // Handle props change
+  const element = getElement(target);
+  const point = getPoint(target);
 
+  React.useEffect(() => {
     if (
       cacheRef.current.element !== element ||
       !isSamePoint(cacheRef.current.point, point) ||
       !isEqual(cacheRef.current.align, align)
     ) {
       forceAlign();
-
-      // Add resize observer
-      if (resizeMonitor.current.element !== element) {
-        resizeMonitor.current.cancel();
-        resizeMonitor.current.element = element;
-        resizeMonitor.current.cancel = monitorResize(element, forceAlign);
-      }
     }
   });
 
+  // Watch popup element resize
+  React.useEffect(() => {
+    const cancelFn = monitorResize(nodeRef.current, forceAlign);
+    return cancelFn;
+  }, [nodeRef.current]);
+
+  // Watch target element resize
+  React.useEffect(() => {
+    const cancelFn = monitorResize(element, forceAlign);
+    return cancelFn;
+  }, [element]);
+
+  // React.useEffect(() => {
+  //   // const element = getElement(target);
+  //   // const point = getPoint(target);
+
+  //   // if (nodeRef.current !== sourceResizeMonitor.current.element) {
+  //   //   sourceResizeMonitor.current.cancel();
+  //   //   sourceResizeMonitor.current.element = nodeRef.current;
+  //   //   sourceResizeMonitor.current.cancel = monitorResize(nodeRef.current, forceAlign);
+  //   // }
+
+  //   if (
+  //     cacheRef.current.element !== element ||
+  //     !isSamePoint(cacheRef.current.point, point) ||
+  //     !isEqual(cacheRef.current.align, align)
+  //   ) {
+  //     forceAlign();
+
+  //     // Add resize observer
+  //     if (resizeMonitor.current.element !== element) {
+  //       resizeMonitor.current.cancel();
+  //       resizeMonitor.current.element = element;
+  //       resizeMonitor.current.cancel = monitorResize(element, forceAlign);
+  //     }
+  //   }
+  // });
+
   // Listen for disabled change
   React.useEffect(() => {
     if (!disabled) {
@@ -156,17 +185,17 @@ const Align: React.ForwardRefRenderFunction<RefAlign, AlignProps> = (
   // Listen for window resize
   React.useEffect(() => {
     if (monitorWindowResize) {
-       const cancelFn = addEventListener(window, 'resize', forceAlign);
+      const cancelFn = addEventListener(window, 'resize', forceAlign);
 
-       return cancelFn;
+      return cancelFn.remove;
     }
   }, [monitorWindowResize]);
 
   // Clear all if unmount
   React.useEffect(
     () => () => {
-      resizeMonitor.current.cancel();
-      sourceResizeMonitor.current.cancel();
+      // resizeMonitor.current.cancel();
+      // sourceResizeMonitor.current.cancel();
       // if (winResizeRef.current) winResizeRef.current.remove();
       cancelForceAlign();
     },

From b055fe3744e9adba3395baaf45f5b3f125f65590 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?=
 <smith3816@gmail.com>
Date: Fri, 16 Dec 2022 16:28:27 +0800
Subject: [PATCH 3/4] chore: clean up

---
 src/Align.tsx         | 43 -------------------------------------------
 tests/strict.test.tsx |  9 +++++----
 2 files changed, 5 insertions(+), 47 deletions(-)

diff --git a/src/Align.tsx b/src/Align.tsx
index 07bbb63..9b64c79 100644
--- a/src/Align.tsx
+++ b/src/Align.tsx
@@ -26,11 +26,6 @@ export interface AlignProps {
   children: React.ReactElement;
 }
 
-// interface MonitorRef {
-//   element?: HTMLElement;
-//   cancel: () => void;
-// }
-
 export interface RefAlign {
   forceAlign: () => void;
 }
@@ -112,15 +107,6 @@ const Align: React.ForwardRefRenderFunction<RefAlign, AlignProps> = (
   }, monitorBufferTime);
 
   // ===================== Effect =====================
-  // Listen for target updated
-  // const resizeMonitor = React.useRef<MonitorRef>({
-  //   cancel: () => {},
-  // });
-  // Listen for source updated
-  // const sourceResizeMonitor = React.useRef<MonitorRef>({
-  //   cancel: () => {},
-  // });
-
   // Handle props change
   const element = getElement(target);
   const point = getPoint(target);
@@ -147,32 +133,6 @@ const Align: React.ForwardRefRenderFunction<RefAlign, AlignProps> = (
     return cancelFn;
   }, [element]);
 
-  // React.useEffect(() => {
-  //   // const element = getElement(target);
-  //   // const point = getPoint(target);
-
-  //   // if (nodeRef.current !== sourceResizeMonitor.current.element) {
-  //   //   sourceResizeMonitor.current.cancel();
-  //   //   sourceResizeMonitor.current.element = nodeRef.current;
-  //   //   sourceResizeMonitor.current.cancel = monitorResize(nodeRef.current, forceAlign);
-  //   // }
-
-  //   if (
-  //     cacheRef.current.element !== element ||
-  //     !isSamePoint(cacheRef.current.point, point) ||
-  //     !isEqual(cacheRef.current.align, align)
-  //   ) {
-  //     forceAlign();
-
-  //     // Add resize observer
-  //     if (resizeMonitor.current.element !== element) {
-  //       resizeMonitor.current.cancel();
-  //       resizeMonitor.current.element = element;
-  //       resizeMonitor.current.cancel = monitorResize(element, forceAlign);
-  //     }
-  //   }
-  // });
-
   // Listen for disabled change
   React.useEffect(() => {
     if (!disabled) {
@@ -194,9 +154,6 @@ const Align: React.ForwardRefRenderFunction<RefAlign, AlignProps> = (
   // Clear all if unmount
   React.useEffect(
     () => () => {
-      // resizeMonitor.current.cancel();
-      // sourceResizeMonitor.current.cancel();
-      // if (winResizeRef.current) winResizeRef.current.remove();
       cancelForceAlign();
     },
     [],
diff --git a/tests/strict.test.tsx b/tests/strict.test.tsx
index 94d4ea1..fa87689 100644
--- a/tests/strict.test.tsx
+++ b/tests/strict.test.tsx
@@ -13,12 +13,10 @@ jest.mock('../src/util', () => {
     ...originUtil,
     monitorResize: (...args: any[]) => {
       (global as any).watchCnt += 1;
-      console.log('mount', (global as any).watchCnt);
       const cancelFn = originUtil.monitorResize(...args);
 
       return () => {
         (global as any).watchCnt -= 1;
-        console.log('unmount', (global as any).watchCnt);
         cancelFn();
       };
     },
@@ -61,7 +59,7 @@ describe('element align', () => {
       );
     };
 
-    render(
+    const { unmount } = render(
       <React.StrictMode>
         <Demo />
       </React.StrictMode>,
@@ -71,7 +69,10 @@ describe('element align', () => {
       jest.runAllTimers();
     });
 
-    console.log('====================================');
+    expect((global as any).watchCnt).toBeGreaterThan(0);
+
+    unmount();
+    expect((global as any).watchCnt).toEqual(0);
   });
 });
 /* eslint-enable */

From 89dc4d9ba1e4a73a8104450dc023ede1eab2a7f8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?=
 <smith3816@gmail.com>
Date: Fri, 16 Dec 2022 16:37:57 +0800
Subject: [PATCH 4/4] fix: align with src

---
 src/Align.tsx | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/Align.tsx b/src/Align.tsx
index 9b64c79..636b493 100644
--- a/src/Align.tsx
+++ b/src/Align.tsx
@@ -72,9 +72,10 @@ const Align: React.ForwardRefRenderFunction<RefAlign, AlignProps> = (
       align: latestAlign,
       onAlign: latestOnAlign,
     } = forceAlignPropsRef.current;
-    if (!latestDisabled && latestTarget) {
-      const source = nodeRef.current;
 
+    const source = nodeRef.current;
+
+    if (!latestDisabled && latestTarget && source) {
       let result: AlignResult;
       const element = getElement(latestTarget);
       const point = getPoint(latestTarget);
