diff --git a/package.json b/package.json index 3d1ff4d..88e14cb 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "docs:build": "dumi build", "docs:deploy": "gh-pages -d .doc", "lint": "eslint src/ --ext .tsx,.ts", + "lint:tsc": "tsc --noEmit", "now-build": "npm run docs:build", "prepare": "husky install", "prepublishOnly": "npm run compile && np --yolo --no-publish", @@ -47,8 +48,8 @@ }, "dependencies": { "@babel/runtime": "^7.11.1", - "classnames": "^2.2.1", - "rc-util": "^5.44.0" + "@rc-component/util": "^1.2.0", + "classnames": "^2.2.1" }, "devDependencies": { "@rc-component/father-plugin": "^1.0.1", diff --git a/src/CSSMotion.tsx b/src/CSSMotion.tsx index 5660e20..8757c0e 100644 --- a/src/CSSMotion.tsx +++ b/src/CSSMotion.tsx @@ -1,11 +1,10 @@ /* eslint-disable react/default-props-match-prop-types, react/no-multi-comp, react/prop-types */ +import { getDOM } from '@rc-component/util/lib/Dom/findDOMNode'; +import { getNodeRef, supportRef } from '@rc-component/util/lib/ref'; import classNames from 'classnames'; -import findDOMNode from 'rc-util/lib/Dom/findDOMNode'; -import { fillRef, getNodeRef, supportRef } from 'rc-util/lib/ref'; import * as React from 'react'; import { useRef } from 'react'; import { Context } from './context'; -import DomWrapper from './DomWrapper'; import useStatus from './hooks/useStatus'; import { isActive } from './hooks/useStepQueue'; import type { @@ -91,7 +90,7 @@ export interface CSSMotionProps { style?: React.CSSProperties; [key: string]: any; }, - ref: (node: any) => void, + ref: React.Ref, ) => React.ReactElement; } @@ -137,22 +136,9 @@ export function genCSSMotion(config: CSSMotionConfig) { // Ref to the react node, it may be a HTMLElement const nodeRef = useRef(); - // Ref to the dom wrapper in case ref can not pass to HTMLElement - const wrapperNodeRef = useRef(); function getDomElement() { - try { - // Here we're avoiding call for findDOMNode since it's deprecated - // in strict mode. We're calling it only when node ref is not - // an instance of DOM HTMLElement. Otherwise use - // findDOMNode as a final resort - return nodeRef.current instanceof HTMLElement - ? nodeRef.current - : findDOMNode(wrapperNodeRef.current); - } catch (e) { - // Only happen when `motionDeadline` trigger but element removed. - return null; - } + return getDOM(nodeRef.current) as HTMLElement; } const [status, statusStep, statusStyle, mergedVisible] = useStatus( @@ -170,13 +156,7 @@ export function genCSSMotion(config: CSSMotionConfig) { } // ====================== Refs ====================== - const setNodeRef = React.useCallback( - (node: any) => { - nodeRef.current = node; - fillRef(ref, node); - }, - [ref], - ); + React.useImperativeHandle(ref, () => getDomElement()); // ===================== Render ===================== let motionChildren: React.ReactNode; @@ -188,16 +168,16 @@ export function genCSSMotion(config: CSSMotionConfig) { } else if (status === STATUS_NONE) { // Stable children if (mergedVisible) { - motionChildren = children({ ...mergedProps }, setNodeRef); + motionChildren = children({ ...mergedProps }, nodeRef); } else if (!removeOnLeave && renderedRef.current && leavedClassName) { motionChildren = children( { ...mergedProps, className: leavedClassName }, - setNodeRef, + nodeRef, ); } else if (forceRender || (!removeOnLeave && !leavedClassName)) { motionChildren = children( { ...mergedProps, style: { display: 'none' } }, - setNodeRef, + nodeRef, ); } else { motionChildren = null; @@ -227,7 +207,7 @@ export function genCSSMotion(config: CSSMotionConfig) { }), style: statusStyle, }, - setNodeRef, + nodeRef, ); } @@ -239,13 +219,13 @@ export function genCSSMotion(config: CSSMotionConfig) { motionChildren = React.cloneElement( motionChildren as React.ReactElement, { - ref: setNodeRef, + ref: nodeRef, }, ); } } - return {motionChildren}; + return motionChildren as React.ReactElement; }); CSSMotion.displayName = 'CSSMotion'; diff --git a/src/CSSMotionList.tsx b/src/CSSMotionList.tsx index 2a3d478..6eedfd1 100644 --- a/src/CSSMotionList.tsx +++ b/src/CSSMotionList.tsx @@ -55,7 +55,7 @@ export interface CSSMotionListProps index?: number; [key: string]: any; }, - ref: (node: any) => void, + ref: React.Ref, ) => React.ReactElement; } diff --git a/src/DomWrapper.tsx b/src/DomWrapper.tsx deleted file mode 100644 index e53c9e5..0000000 --- a/src/DomWrapper.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import * as React from 'react'; - -export interface DomWrapperProps { - children: React.ReactNode; -} - -class DomWrapper extends React.Component { - render() { - return this.props.children; - } -} - -export default DomWrapper; diff --git a/src/hooks/useIsomorphicLayoutEffect.ts b/src/hooks/useIsomorphicLayoutEffect.ts index 7dd0ab0..e2a6211 100644 --- a/src/hooks/useIsomorphicLayoutEffect.ts +++ b/src/hooks/useIsomorphicLayoutEffect.ts @@ -1,4 +1,4 @@ -import canUseDom from 'rc-util/lib/Dom/canUseDom'; +import canUseDom from '@rc-component/util/lib/Dom/canUseDom'; import { useEffect, useLayoutEffect } from 'react'; // It's safe to use `useLayoutEffect` but the warning is annoying diff --git a/src/hooks/useNextFrame.ts b/src/hooks/useNextFrame.ts index 9f06173..0ee9604 100644 --- a/src/hooks/useNextFrame.ts +++ b/src/hooks/useNextFrame.ts @@ -1,4 +1,4 @@ -import raf from 'rc-util/lib/raf'; +import raf from '@rc-component/util/lib/raf'; import * as React from 'react'; export default (): [ diff --git a/src/hooks/useStatus.ts b/src/hooks/useStatus.ts index 73014ef..da39f0c 100644 --- a/src/hooks/useStatus.ts +++ b/src/hooks/useStatus.ts @@ -1,6 +1,6 @@ -import { useEvent } from 'rc-util'; -import useState from 'rc-util/lib/hooks/useState'; -import useSyncState from 'rc-util/lib/hooks/useSyncState'; +import { useEvent } from '@rc-component/util'; +import useState from '@rc-component/util/lib/hooks/useState'; +import useSyncState from '@rc-component/util/lib/hooks/useSyncState'; import * as React from 'react'; import { useEffect, useRef } from 'react'; import type { CSSMotionProps } from '../CSSMotion'; diff --git a/src/hooks/useStepQueue.ts b/src/hooks/useStepQueue.ts index 48a4605..05362c0 100644 --- a/src/hooks/useStepQueue.ts +++ b/src/hooks/useStepQueue.ts @@ -1,4 +1,4 @@ -import useState from 'rc-util/lib/hooks/useState'; +import useState from '@rc-component/util/lib/hooks/useState'; import * as React from 'react'; import type { MotionStatus, StepStatus } from '../interface'; import { diff --git a/src/util/motion.ts b/src/util/motion.ts index a9d3752..d2a60d9 100644 --- a/src/util/motion.ts +++ b/src/util/motion.ts @@ -1,5 +1,5 @@ -import canUseDOM from 'rc-util/lib/Dom/canUseDom'; -import { MotionName } from '../CSSMotion'; +import canUseDOM from '@rc-component/util/lib/Dom/canUseDom'; +import type { MotionName } from '../CSSMotion'; // ================= Transition ================= // Event wrapper. Copy from react source code diff --git a/tests/CSSMotion.spec.tsx b/tests/CSSMotion.spec.tsx index b5c71a9..d724fdd 100644 --- a/tests/CSSMotion.spec.tsx +++ b/tests/CSSMotion.spec.tsx @@ -288,6 +288,9 @@ describe('CSSMotion', () => { }); describe('deadline should work', () => { + // NOTE: only test for not crash here + // Since React 19 not support `findDOMNode` anymore + // the func call will not get real DOM node function test(name: string, Component: React.ComponentType) { it(name, () => { const onAppearEnd = jest.fn(); @@ -839,10 +842,11 @@ describe('CSSMotion', () => { jest.resetAllMocks(); }); - it('calls findDOMNode when no refs are passed', () => { + it('not crash when no refs are passed', () => { const Div = () =>
; + const cssMotionRef = React.createRef(); render( - + {() =>
} , ); @@ -851,7 +855,8 @@ describe('CSSMotion', () => { jest.runAllTimers(); }); - expect(ReactDOM.findDOMNode).toHaveBeenCalled(); + expect(cssMotionRef.current).toBeFalsy(); + expect(ReactDOM.findDOMNode).not.toHaveBeenCalled(); }); it('does not call findDOMNode when ref is passed internally', () => { @@ -868,11 +873,24 @@ describe('CSSMotion', () => { expect(ReactDOM.findDOMNode).not.toHaveBeenCalled(); }); - it('calls findDOMNode when refs are forwarded but not assigned', () => { + it('support nativeElement of ref', () => { const domRef = React.createRef(); - const Div = () =>
; + const Div = React.forwardRef< + { + nativeElement: HTMLDivElement; + }, + object + >((props, ref) => { + const divRef = React.useRef(null); - render( + React.useImperativeHandle(ref, () => ({ + nativeElement: divRef.current!, + })); + + return
; + }); + + const { container } = render( {() =>
} , @@ -882,7 +900,8 @@ describe('CSSMotion', () => { jest.runAllTimers(); }); - expect(ReactDOM.findDOMNode).toHaveBeenCalled(); + expect(domRef.current).toBe(container.querySelector('.bamboo')); + expect(ReactDOM.findDOMNode).not.toHaveBeenCalled(); }); it('does not call findDOMNode when refs are forwarded and assigned', () => {