Skip to content

Commit 30b868a

Browse files
adaladamsamat
andauthored
calling deprecated findDOMNode only when it is necessary (#17)
* avoiding call for deprecated findDOMNode as much as possible; removed some excessive refs * added tests for strict mode deprecated findDOMNode call checks Co-authored-by: samat <samat@dereknet.com>
1 parent a5e107c commit 30b868a

File tree

2 files changed

+92
-22
lines changed

2 files changed

+92
-22
lines changed

src/CSSMotion.tsx

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,9 @@ import type {
99
MotionStatus,
1010
MotionEventHandler,
1111
MotionEndEventHandler,
12-
MotionPrepareEventHandler} from './interface';
13-
import {
14-
STATUS_NONE,
15-
STEP_PREPARE,
16-
STEP_START,
12+
MotionPrepareEventHandler,
1713
} from './interface';
14+
import { STATUS_NONE, STEP_PREPARE, STEP_START } from './interface';
1815
import useStatus from './hooks/useStatus';
1916
import DomWrapper from './DomWrapper';
2017
import { isActive } from './hooks/useStepQueue';
@@ -135,15 +132,19 @@ export function genCSSMotion(
135132
const supportMotion = isSupportTransition(props);
136133

137134
// Ref to the react node, it may be a HTMLElement
138-
const nodeRef = useRef();
135+
const nodeRef = useRef<any>();
139136
// Ref to the dom wrapper in case ref can not pass to HTMLElement
140137
const wrapperNodeRef = useRef();
141138

142139
function getDomElement() {
143140
try {
144-
return findDOMNode<HTMLElement>(
145-
nodeRef.current || wrapperNodeRef.current,
146-
);
141+
// Here we're avoiding call for findDOMNode since it's deprecated
142+
// in strict mode. We're calling it only when node ref is not
143+
// an instance of DOM HTMLElement. Otherwise use
144+
// findDOMNode as a final resort
145+
return nodeRef.current instanceof HTMLElement
146+
? nodeRef.current
147+
: findDOMNode<HTMLElement>(wrapperNodeRef.current);
147148
} catch (e) {
148149
// Only happen when `motionDeadline` trigger but element removed.
149150
return null;
@@ -157,21 +158,17 @@ export function genCSSMotion(
157158
props,
158159
);
159160

160-
// Record whether content has rended
161+
// Record whether content has rendered
161162
// Will return null for un-rendered even when `removeOnLeave={false}`
162163
const renderedRef = React.useRef(mergedVisible);
163164
if (mergedVisible) {
164165
renderedRef.current = true;
165166
}
166167

167168
// ====================== Refs ======================
168-
const originRef = useRef(ref);
169-
originRef.current = ref;
170-
171169
const setNodeRef = React.useCallback((node: any) => {
172170
nodeRef.current = node;
173-
174-
fillRef(originRef.current, node);
171+
fillRef(ref, node);
175172
}, []);
176173

177174
// ===================== Render =====================
@@ -213,10 +210,8 @@ export function genCSSMotion(
213210
{
214211
...mergedProps,
215212
className: classNames(getTransitionName(motionName, status), {
216-
[getTransitionName(
217-
motionName,
218-
`${status}-${statusSuffix}`,
219-
)]: statusSuffix,
213+
[getTransitionName(motionName, `${status}-${statusSuffix}`)]:
214+
statusSuffix,
220215
[motionName as string]: typeof motionName === 'string',
221216
}),
222217
style: statusStyle,

tests/CSSMotion.spec.tsx

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import classNames from 'classnames';
88
import { mount } from './wrapper';
99
import type { CSSMotionProps } from '../src/CSSMotion';
1010
import RefCSSMotion, { genCSSMotion } from '../src/CSSMotion';
11+
import ReactDOM from 'react-dom';
1112

1213
describe('CSSMotion', () => {
1314
const CSSMotion = genCSSMotion({
@@ -638,9 +639,9 @@ describe('CSSMotion', () => {
638639
jest.runAllTimers();
639640

640641
const transitionEndEvent = new Event('transitionend');
641-
((wrapper
642-
.find('.motion-box')
643-
.instance() as any) as HTMLElement).dispatchEvent(transitionEndEvent);
642+
(
643+
wrapper.find('.motion-box').instance() as any as HTMLElement
644+
).dispatchEvent(transitionEndEvent);
644645

645646
jest.runAllTimers();
646647

@@ -649,4 +650,78 @@ describe('CSSMotion', () => {
649650
expect(wrapper.find('.motion-box')).toHaveLength(1);
650651
expect(wrapper.find('.motion-box').hasClass('removed')).toBeTruthy();
651652
});
653+
654+
describe('strict mode', () => {
655+
beforeEach(() => {
656+
jest.spyOn(ReactDOM, 'findDOMNode');
657+
});
658+
659+
afterEach(() => {
660+
jest.resetAllMocks();
661+
});
662+
663+
it('calls findDOMNode when no refs are passed', () => {
664+
const wrapper = mount(
665+
<CSSMotion motionName="transition" visible>
666+
{() => <div />}
667+
</CSSMotion>,
668+
);
669+
670+
act(() => {
671+
jest.runAllTimers();
672+
wrapper.update();
673+
});
674+
675+
expect(ReactDOM.findDOMNode).toHaveBeenCalled();
676+
});
677+
678+
it('does not call findDOMNode when ref is passed internally', () => {
679+
const wrapper = mount(
680+
<CSSMotion motionName="transition" visible>
681+
{(props, ref) => <div ref={ref} />}
682+
</CSSMotion>,
683+
);
684+
685+
act(() => {
686+
jest.runAllTimers();
687+
wrapper.update();
688+
});
689+
690+
expect(ReactDOM.findDOMNode).not.toHaveBeenCalled();
691+
});
692+
693+
it('calls findDOMNode when refs are forwarded but not assigned', () => {
694+
const domRef = React.createRef();
695+
696+
const wrapper = mount(
697+
<CSSMotion motionName="transition" visible ref={domRef}>
698+
{() => <div />}
699+
</CSSMotion>,
700+
);
701+
702+
act(() => {
703+
jest.runAllTimers();
704+
wrapper.update();
705+
});
706+
707+
expect(ReactDOM.findDOMNode).toHaveBeenCalled();
708+
});
709+
710+
it('does not call findDOMNode when refs are forwarded and assigned', () => {
711+
const domRef = React.createRef();
712+
713+
const wrapper = mount(
714+
<CSSMotion motionName="transition" visible ref={domRef}>
715+
{(props, ref) => <div ref={ref} />}
716+
</CSSMotion>,
717+
);
718+
719+
act(() => {
720+
jest.runAllTimers();
721+
wrapper.update();
722+
});
723+
724+
expect(ReactDOM.findDOMNode).not.toHaveBeenCalled();
725+
});
726+
});
652727
});

0 commit comments

Comments
 (0)