Skip to content

Commit 1e7b371

Browse files
committed
test(useforkref): add tests for useForkRef hook
1 parent 0ba2445 commit 1e7b371

File tree

1 file changed

+131
-0
lines changed

1 file changed

+131
-0
lines changed
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/* eslint-disable react/require-default-props */
2+
/* eslint-disable react/forbid-prop-types */
3+
import React from 'react';
4+
import { render } from '@testing-library/react';
5+
import propTypes from 'prop-types';
6+
7+
import useForkRef from './useForkRef';
8+
9+
const consoleError = console.error;
10+
11+
beforeEach(() => {
12+
console.error = jest.fn();
13+
});
14+
15+
afterEach(() => {
16+
console.error = consoleError;
17+
});
18+
19+
describe('useForkRef', () => {
20+
it('returns a single ref-setter function that forks the ref to its inputs', () => {
21+
function Component(props) {
22+
const { innerRef } = props;
23+
const ownRef = React.useRef(null);
24+
const [, forceUpdate] = React.useState(0);
25+
React.useEffect(() => forceUpdate(n => !n), []);
26+
27+
const handleRef = useForkRef(innerRef, ownRef);
28+
29+
return (
30+
<div ref={handleRef}>{ownRef.current ? 'has a ref' : 'has no ref'}</div>
31+
);
32+
}
33+
34+
Component.propTypes = {
35+
innerRef: propTypes.any
36+
};
37+
38+
const outerRef = React.createRef();
39+
render(<Component innerRef={outerRef} />);
40+
41+
expect(outerRef.current.textContent).toBe('has a ref');
42+
expect(console.error).not.toHaveBeenCalled();
43+
});
44+
45+
it('forks if only one of the branches requires a ref', () => {
46+
const Component = React.forwardRef(function Component(props, ref) {
47+
const [hasRef, setHasRef] = React.useState(false);
48+
const handleOwnRef = React.useCallback(() => setHasRef(true), []);
49+
const handleRef = useForkRef(handleOwnRef, ref);
50+
51+
return <div ref={handleRef}>{String(hasRef)}</div>;
52+
});
53+
54+
const { getByText } = render(<Component />);
55+
56+
expect(getByText('true')).toBeInTheDocument();
57+
expect(console.error).not.toHaveBeenCalled();
58+
});
59+
60+
it('does nothing if none of the forked branches requires a ref', () => {
61+
const Outer = React.forwardRef(function Outer(props, ref) {
62+
const { children } = props;
63+
const handleRef = useForkRef(children.ref, ref);
64+
65+
return React.cloneElement(children, { ref: handleRef });
66+
});
67+
68+
Outer.propTypes = { children: propTypes.element.isRequired };
69+
70+
function Inner() {
71+
return <div />;
72+
}
73+
74+
render(
75+
<Outer>
76+
<Inner />
77+
</Outer>
78+
);
79+
expect(console.error).not.toHaveBeenCalled();
80+
});
81+
82+
describe('changing refs', () => {
83+
function Div(props) {
84+
const { leftRef, rightRef, ...other } = props;
85+
const handleRef = useForkRef(leftRef, rightRef);
86+
87+
return <div {...other} ref={handleRef} />;
88+
}
89+
90+
Div.propTypes = {
91+
leftRef: propTypes.oneOfType([propTypes.func, propTypes.object]),
92+
rightRef: propTypes.oneOfType([propTypes.func, propTypes.object])
93+
};
94+
95+
it('handles changing from no ref to some ref', () => {
96+
const { rerender } = render(<Div id='test' />);
97+
98+
expect(console.error).not.toHaveBeenCalled();
99+
100+
const ref = React.createRef();
101+
rerender(<Div id='test' leftRef={ref} />);
102+
103+
expect(ref.current.id).toBe('test');
104+
expect(console.error).not.toHaveBeenCalled();
105+
});
106+
107+
it('cleans up detached refs', () => {
108+
const firstLeftRef = React.createRef();
109+
const firstRightRef = React.createRef();
110+
const secondRightRef = React.createRef();
111+
112+
const { rerender } = render(
113+
<Div leftRef={firstLeftRef} rightRef={firstRightRef} id='test' />
114+
);
115+
116+
expect(console.error).not.toHaveBeenCalled();
117+
118+
expect(firstLeftRef.current.id).toBe('test');
119+
expect(firstRightRef.current.id).toBe('test');
120+
expect(secondRightRef.current).toBe(null);
121+
122+
rerender(
123+
<Div leftRef={firstLeftRef} rightRef={secondRightRef} id='test' />
124+
);
125+
126+
expect(firstLeftRef.current.id).toBe('test');
127+
expect(firstRightRef.current).toBe(null);
128+
expect(secondRightRef.current.id).toBe('test');
129+
});
130+
});
131+
});

0 commit comments

Comments
 (0)