From 0747bebfe4e451ac694ffb6c656fdd75e949017d 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?= Date: Wed, 15 May 2024 17:37:22 +0800 Subject: [PATCH 1/6] chore: tmp of it --- package.json | 4 +-- src/hooks/useStatus.ts | 3 +++ tests/CSSMotion.spec.tsx | 55 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0e68872..c1127f7 100644 --- a/package.json +++ b/package.json @@ -69,8 +69,8 @@ "np": "^6.2.4", "prettier": "^2.1.1", "rc-test": "^7.0.14", - "react": "^18.0.0", - "react-dom": "^18.0.0", + "react": "^18.3.0", + "react-dom": "^18.3.0", "typescript": "^4.0.3" }, "peerDependencies": { diff --git a/src/hooks/useStatus.ts b/src/hooks/useStatus.ts index 1250055..11650c5 100644 --- a/src/hooks/useStatus.ts +++ b/src/hooks/useStatus.ts @@ -73,6 +73,7 @@ export default function useStatus( } function onInternalMotionEnd(event: MotionEvent) { + console.log('Motion End!'); const element = getDomElement(); if (event && !event.deadline && event.target !== element) { // event exists @@ -156,8 +157,10 @@ export default function useStatus( patchMotionEvents(getDomElement()); if (motionDeadline > 0) { + console.log('!!!'); clearTimeout(deadlineRef.current); deadlineRef.current = setTimeout(() => { + console.log('???'); onInternalMotionEnd({ deadline: true, } as MotionEvent); diff --git a/tests/CSSMotion.spec.tsx b/tests/CSSMotion.spec.tsx index f822936..6090f20 100644 --- a/tests/CSSMotion.spec.tsx +++ b/tests/CSSMotion.spec.tsx @@ -342,6 +342,61 @@ describe('CSSMotion', () => { return
; }), ); + + it('not warning on StrictMode', () => { + const onLeaveEnd = jest.fn(); + const errorSpy = jest.spyOn(console, 'error'); + + const renderDemo = (visible: boolean) => ( + + + {({ style, className }) => ( +
+ )} + + + ); + + const { rerender, container } = render(renderDemo(true)); + act(() => { + jest.advanceTimersByTime(100000); + }); + + // Leave + rerender(renderDemo(false)); + act(() => { + jest.advanceTimersByTime(500); + }); + console.log(container.innerHTML); + + // Motion end + fireEvent.transitionEnd( + container.querySelector('.transition-leave-active'), + ); + act(() => { + jest.advanceTimersByTime(100); + }); + + // Another timeout + act(() => { + jest.advanceTimersByTime(1000); + }); + + expect(onLeaveEnd).toHaveBeenCalledTimes(1); + expect(errorSpy).not.toHaveBeenCalled(); + + errorSpy.mockRestore(); + }); }); it('not crash when no children', () => { From 8bd2f3a038f2fa0a399a47a3c70524a5192ecad1 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?= Date: Wed, 15 May 2024 17:42:34 +0800 Subject: [PATCH 2/6] chore: update deps --- .gitignore | 1 + package.json | 2 +- tests/CSSMotion.spec.tsx | 3 +-- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 2c2d950..03ee30f 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ coverage/ .dumi/tmp .dumi/tmp-production .dumi/tmp-test +.node diff --git a/package.json b/package.json index c1127f7..a6fa849 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "devDependencies": { "@rc-component/father-plugin": "^1.0.1", "@testing-library/jest-dom": "^5.16.4", - "@testing-library/react": "^13.0.0", + "@testing-library/react": "^15.0.7", "@types/classnames": "^2.2.9", "@types/jest": "^26.0.8", "@types/react": "^16.9.2", diff --git a/tests/CSSMotion.spec.tsx b/tests/CSSMotion.spec.tsx index 6090f20..6eef9b4 100644 --- a/tests/CSSMotion.spec.tsx +++ b/tests/CSSMotion.spec.tsx @@ -2,11 +2,10 @@ react/no-render-return-value, max-classes-per-file, react/prefer-stateless-function, react/no-multi-comp */ -import { fireEvent, render } from '@testing-library/react'; +import { act, fireEvent, render } from '@testing-library/react'; import classNames from 'classnames'; import React from 'react'; import ReactDOM from 'react-dom'; -import { act } from 'react-dom/test-utils'; import type { CSSMotionProps } from '../src'; import { Provider } from '../src'; import RefCSSMotion, { genCSSMotion } from '../src/CSSMotion'; From e2e0c50d713f803359def783ed77eeadc78031c4 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?= Date: Wed, 15 May 2024 17:59:30 +0800 Subject: [PATCH 3/6] chore: useEvent --- package.json | 2 +- src/hooks/useDomMotionEvents.ts | 11 +---------- src/hooks/useStatus.ts | 19 +++++++++++++------ tests/CSSMotion.spec.tsx | 1 - 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index a6fa849..66ae627 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "dependencies": { "@babel/runtime": "^7.11.1", "classnames": "^2.2.1", - "rc-util": "^5.21.0" + "rc-util": "^5.39.3" }, "devDependencies": { "@rc-component/father-plugin": "^1.0.1", diff --git a/src/hooks/useDomMotionEvents.ts b/src/hooks/useDomMotionEvents.ts index 20d7bb3..0ec0517 100644 --- a/src/hooks/useDomMotionEvents.ts +++ b/src/hooks/useDomMotionEvents.ts @@ -5,19 +5,10 @@ import type { MotionEvent } from '../interface'; import { animationEndName, transitionEndName } from '../util/motion'; export default ( - callback: (event: MotionEvent) => void, + onInternalMotionEnd: (event: MotionEvent) => void, ): [(element: HTMLElement) => void, (element: HTMLElement) => void] => { const cacheElementRef = useRef(); - // Cache callback - const callbackRef = useRef(callback); - callbackRef.current = callback; - - // Internal motion event handler - const onInternalMotionEnd = React.useCallback((event: MotionEvent) => { - callbackRef.current(event); - }, []); - // Remove events function removeMotionEvents(element: HTMLElement) { if (element) { diff --git a/src/hooks/useStatus.ts b/src/hooks/useStatus.ts index 11650c5..38e2676 100644 --- a/src/hooks/useStatus.ts +++ b/src/hooks/useStatus.ts @@ -1,3 +1,4 @@ +import { useEvent } from 'rc-util'; import useState from 'rc-util/lib/hooks/useState'; import * as React from 'react'; import { useEffect, useRef } from 'react'; @@ -72,8 +73,13 @@ export default function useStatus( setStyle(null, true); } - function onInternalMotionEnd(event: MotionEvent) { - console.log('Motion End!'); + const onInternalMotionEnd = useEvent((event: MotionEvent) => { + // Do nothing since not in any transition status. + // This may happen when `motionDeadline` trigger. + if (status === STATUS_NONE) { + return; + } + const element = getDomElement(); if (event && !event.deadline && event.target !== element) { // event exists @@ -84,6 +90,8 @@ export default function useStatus( const currentActive = activeRef.current; + console.log('Motion End!', status, currentActive); + let canEnd: boolean | void; if (status === STATUS_APPEAR && currentActive) { canEnd = onAppearEnd?.(element, event); @@ -94,10 +102,10 @@ export default function useStatus( } // Only update status when `canEnd` and not destroyed - if (status !== STATUS_NONE && currentActive && canEnd !== false) { + if (currentActive && canEnd !== false) { updateMotionEndStatus(); } - } + }); const [patchMotionEvents] = useDomMotionEvents(onInternalMotionEnd); @@ -153,14 +161,13 @@ export default function useStatus( } if (step === STEP_ACTIVE) { + console.log('Patch Events:', step, status); // Patch events when motion needed patchMotionEvents(getDomElement()); if (motionDeadline > 0) { - console.log('!!!'); clearTimeout(deadlineRef.current); deadlineRef.current = setTimeout(() => { - console.log('???'); onInternalMotionEnd({ deadline: true, } as MotionEvent); diff --git a/tests/CSSMotion.spec.tsx b/tests/CSSMotion.spec.tsx index 6eef9b4..2ead8a8 100644 --- a/tests/CSSMotion.spec.tsx +++ b/tests/CSSMotion.spec.tsx @@ -376,7 +376,6 @@ describe('CSSMotion', () => { act(() => { jest.advanceTimersByTime(500); }); - console.log(container.innerHTML); // Motion end fireEvent.transitionEnd( From caa4440f18f62527a2e861521b41d1be9f4fa1d5 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?= Date: Wed, 15 May 2024 18:00:46 +0800 Subject: [PATCH 4/6] fix: status --- src/hooks/useStatus.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/hooks/useStatus.ts b/src/hooks/useStatus.ts index 38e2676..fa79cc8 100644 --- a/src/hooks/useStatus.ts +++ b/src/hooks/useStatus.ts @@ -90,8 +90,6 @@ export default function useStatus( const currentActive = activeRef.current; - console.log('Motion End!', status, currentActive); - let canEnd: boolean | void; if (status === STATUS_APPEAR && currentActive) { canEnd = onAppearEnd?.(element, event); @@ -160,8 +158,7 @@ export default function useStatus( setStyle(eventHandlers[step]?.(getDomElement(), null) || null); } - if (step === STEP_ACTIVE) { - console.log('Patch Events:', step, status); + if (step === STEP_ACTIVE && status !== STATUS_NONE) { // Patch events when motion needed patchMotionEvents(getDomElement()); From 8bb6303b50c7619eeba73d298991adfa834c084d 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?= Date: Thu, 16 May 2024 10:11:18 +0800 Subject: [PATCH 5/6] chore: fix ci --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 66ae627..09f8a43 100644 --- a/package.json +++ b/package.json @@ -56,8 +56,8 @@ "@testing-library/react": "^15.0.7", "@types/classnames": "^2.2.9", "@types/jest": "^26.0.8", - "@types/react": "^16.9.2", - "@types/react-dom": "^16.9.0", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", "@umijs/fabric": "^2.0.8", "cross-env": "^7.0.2", "dumi": "^2.0.18", From 7f1534642c4cb205e59198464ae340566f483caa 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?= Date: Thu, 16 May 2024 10:39:55 +0800 Subject: [PATCH 6/6] chore: fix ts --- src/CSSMotion.tsx | 4 +--- src/util/diff.ts | 8 +++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/CSSMotion.tsx b/src/CSSMotion.tsx index cc64a48..bff0e6c 100644 --- a/src/CSSMotion.tsx +++ b/src/CSSMotion.tsx @@ -107,9 +107,7 @@ export interface CSSMotionState { * `transitionSupport` is used for none transition test case. * Default we use browser transition event support check. */ -export function genCSSMotion( - config: CSSMotionConfig, -): React.ForwardRefExoticComponent }> { +export function genCSSMotion(config: CSSMotionConfig) { let transitionSupport = config; if (typeof config === 'object') { diff --git a/src/util/diff.ts b/src/util/diff.ts index 96b88a9..d3d30f6 100644 --- a/src/util/diff.ts +++ b/src/util/diff.ts @@ -8,8 +8,10 @@ export type DiffStatus = | typeof STATUS_REMOVE | typeof STATUS_REMOVED; +type RawKeyType = string | number; + export interface KeyObject { - key: React.Key; + key: RawKeyType; status?: DiffStatus; } @@ -18,7 +20,7 @@ export function wrapKeyToObject(key: React.Key | KeyObject) { if (key && typeof key === 'object' && 'key' in key) { keyObj = key; } else { - keyObj = { key: key as React.Key }; + keyObj = { key: key as RawKeyType }; } return { ...keyObj, @@ -90,7 +92,7 @@ export function diffKeys( * Merge same key when it remove and add again: * [1 - add, 2 - keep, 1 - remove] -> [1 - keep, 2 - keep] */ - const keys = {}; + const keys: Record = {}; list.forEach(({ key }) => { keys[key] = (keys[key] || 0) + 1; });