Skip to content

Commit 139d656

Browse files
authored
feat: Auto pass ref if children validate (#28)
* chore: update deps & new test case * chore: react way * chore: clean up * test: patch ref test
1 parent 78e16f1 commit 139d656

File tree

8 files changed

+92
-20
lines changed

8 files changed

+92
-20
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
"dependencies": {
4444
"@babel/runtime": "^7.11.1",
4545
"classnames": "^2.2.1",
46-
"rc-util": "^5.19.2"
46+
"rc-util": "^5.21.0"
4747
},
4848
"devDependencies": {
4949
"@testing-library/jest-dom": "^5.16.4",

src/CSSMotion.tsx

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import * as React from 'react';
33
import { useRef } from 'react';
44
import findDOMNode from 'rc-util/lib/Dom/findDOMNode';
5-
import { fillRef } from 'rc-util/lib/ref';
5+
import { fillRef, supportRef } from 'rc-util/lib/ref';
66
import classNames from 'classnames';
77
import { getTransitionName, supportTransition } from './util/motion';
88
import type {
@@ -166,10 +166,13 @@ export function genCSSMotion(
166166
}
167167

168168
// ====================== Refs ======================
169-
const setNodeRef = React.useCallback((node: any) => {
170-
nodeRef.current = node;
171-
fillRef(ref, node);
172-
}, []);
169+
const setNodeRef = React.useCallback(
170+
(node: any) => {
171+
nodeRef.current = node;
172+
fillRef(ref, node);
173+
},
174+
[ref],
175+
);
173176

174177
// ===================== Render =====================
175178
let motionChildren: React.ReactNode;
@@ -220,6 +223,17 @@ export function genCSSMotion(
220223
);
221224
}
222225

226+
// Auto inject ref if child node not have `ref` props
227+
if (React.isValidElement(motionChildren) && supportRef(motionChildren)) {
228+
const { ref: originNodeRef } = motionChildren as any;
229+
230+
if (!originNodeRef) {
231+
motionChildren = React.cloneElement(motionChildren, {
232+
ref: setNodeRef,
233+
});
234+
}
235+
}
236+
223237
return <DomWrapper ref={wrapperNodeRef}>{motionChildren}</DomWrapper>;
224238
});
225239

src/hooks/useDomMotionEvents.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as React from 'react';
22
import { useRef } from 'react';
33

44
import { animationEndName, transitionEndName } from '../util/motion';
5-
import { MotionEvent } from '../interface';
5+
import type { MotionEvent } from '../interface';
66

77
export default (
88
callback: (event: MotionEvent) => void,

src/hooks/useIsomorphicLayoutEffect.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.

src/hooks/useStatus.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as React from 'react';
22
import { useRef, useEffect } from 'react';
33
import useState from 'rc-util/lib/hooks/useState';
4+
import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
45
import {
56
STATUS_APPEAR,
67
STATUS_NONE,
@@ -18,7 +19,6 @@ import type {
1819
StepStatus,
1920
} from '../interface';
2021
import type { CSSMotionProps } from '../CSSMotion';
21-
import useIsomorphicLayoutEffect from './useIsomorphicLayoutEffect';
2222
import useStepQueue, { DoStep, SkipStep, isActive } from './useStepQueue';
2323
import useDomMotionEvents from './useDomMotionEvents';
2424

@@ -163,7 +163,7 @@ export default function useStatus(
163163

164164
// ============================ Status ============================
165165
// Update with new status
166-
useIsomorphicLayoutEffect(() => {
166+
useLayoutEffect(() => {
167167
setAsyncVisible(visible);
168168

169169
const isMounted = mountedRef.current;

src/hooks/useStepQueue.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as React from 'react';
22
import useState from 'rc-util/lib/hooks/useState';
3+
import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
34
import type { StepStatus, MotionStatus } from '../interface';
45
import {
56
STEP_PREPARE,
@@ -8,7 +9,6 @@ import {
89
STEP_ACTIVATED,
910
STEP_NONE,
1011
} from '../interface';
11-
import useIsomorphicLayoutEffect from './useIsomorphicLayoutEffect';
1212
import useNextFrame from './useNextFrame';
1313

1414
const STEP_QUEUE: StepStatus[] = [
@@ -41,7 +41,7 @@ export default (
4141
setStep(STEP_PREPARE, true);
4242
}
4343

44-
useIsomorphicLayoutEffect(() => {
44+
useLayoutEffect(() => {
4545
if (step !== STEP_NONE && step !== STEP_ACTIVATED) {
4646
const index = STEP_QUEUE.indexOf(step);
4747
const nextStep = STEP_QUEUE[index + 1];

tests/CSSMotion.spec.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -659,9 +659,10 @@ describe('CSSMotion', () => {
659659
});
660660

661661
it('calls findDOMNode when no refs are passed', () => {
662+
const Div = () => <div />;
662663
render(
663664
<CSSMotion motionName="transition" visible>
664-
{() => <div />}
665+
{() => <Div />}
665666
</CSSMotion>,
666667
);
667668

@@ -688,9 +689,11 @@ describe('CSSMotion', () => {
688689

689690
it('calls findDOMNode when refs are forwarded but not assigned', () => {
690691
const domRef = React.createRef();
692+
const Div = () => <div />;
693+
691694
render(
692695
<CSSMotion motionName="transition" visible ref={domRef}>
693-
{() => <div />}
696+
{() => <Div />}
694697
</CSSMotion>,
695698
);
696699

tests/StrictMode.spec.tsx

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/* eslint-disable
2+
react/no-render-return-value, max-classes-per-file,
3+
react/prefer-stateless-function, react/no-multi-comp
4+
*/
5+
import React from 'react';
6+
import { act } from 'react-dom/test-utils';
7+
import classNames from 'classnames';
8+
import { render, fireEvent } from '@testing-library/react';
9+
// import type { CSSMotionProps } from '../src/CSSMotion';
10+
import { genCSSMotion } from '../src/CSSMotion';
11+
// import RefCSSMotion, { genCSSMotion } from '../src/CSSMotion';
12+
// import ReactDOM from 'react-dom';
13+
14+
describe('StrictMode', () => {
15+
const CSSMotion = genCSSMotion({
16+
transitionSupport: true,
17+
});
18+
19+
beforeEach(() => {
20+
jest.useFakeTimers();
21+
});
22+
23+
afterEach(() => {
24+
jest.clearAllTimers();
25+
jest.useRealTimers();
26+
});
27+
28+
it('motion should end', () => {
29+
const ref = React.createRef();
30+
31+
const { container } = render(
32+
<React.StrictMode>
33+
<CSSMotion motionName="transition" ref={ref} motionAppear visible>
34+
{({ style, className }) => {
35+
return (
36+
<div
37+
style={style}
38+
className={classNames('motion-box', className)}
39+
/>
40+
);
41+
}}
42+
</CSSMotion>
43+
</React.StrictMode>,
44+
);
45+
46+
const node = container.querySelector('.motion-box');
47+
expect(node).toHaveClass('transition-appear', 'transition-appear-start');
48+
49+
// Active
50+
act(() => {
51+
jest.runAllTimers();
52+
});
53+
expect(node).not.toHaveClass('transition-appear-start');
54+
expect(node).toHaveClass('transition-appear-active');
55+
56+
// Trigger End
57+
fireEvent.transitionEnd(node);
58+
expect(node).not.toHaveClass('transition-appear');
59+
60+
expect(ref.current).toBe(node);
61+
});
62+
});

0 commit comments

Comments
 (0)