Skip to content

Commit c5a6feb

Browse files
authored
Merge pull request #12 from lensapp/fix-invalid-refs-multiple-times
fix: Ensure that callback style refs on ElementComponents are only called when changed
2 parents 108b47f + 0a523f7 commit c5a6feb

File tree

2 files changed

+42
-42
lines changed

2 files changed

+42
-42
lines changed

packages/element-component/src/element-component.test.tsx

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,10 @@ describe('element', () => {
126126
const Div = getElementComponent('div');
127127

128128
const TestComponent = () => {
129-
const testRef = React.useRef(null);
129+
const testRef = React.useRef<HTMLDivElement>(null);
130130

131131
useEffect(() => {
132-
testRef.current.classList.add('some-class-from-ref');
132+
testRef.current!.classList.add('some-class-from-ref');
133133
});
134134

135135
return <Div ref={testRef} data-some-element-test />;
@@ -167,35 +167,43 @@ describe('element', () => {
167167
).toBe(true);
168168
});
169169

170+
it('given functional ref, when rendered, when rerendered with a different prop changed, does not call again', () => {
171+
const Div = getElementComponent('div');
172+
const someRef = jest.fn();
173+
174+
const rendered = render(<Div ref={someRef} data-some-element-test />);
175+
176+
someRef.mockClear();
177+
rendered.rerender(<Div ref={someRef} />);
178+
179+
expect(someRef).not.toHaveBeenCalled();
180+
});
181+
170182
describe('given multiple plugins with ref', () => {
171183
let rendered: RenderResult;
172184
let discover: Discover;
173185

174186
beforeEach(() => {
175-
const somePlugin1 = getPlugin<{ $somePlugin1Input?: boolean }>(
176-
(props: any): any => {
177-
const ref = useRef(null);
178-
179-
useEffect(() => {
180-
ref.current.classList.add('some-class-from-hook-ref');
181-
}, []);
182-
183-
return {
184-
$somePlugin1Input: undefined,
185-
ref,
186-
};
187-
},
188-
);
187+
const somePlugin1 = getPlugin<{ $somePlugin1Input?: boolean }>(() => {
188+
const ref = useRef<HTMLElement>(null);
189189

190-
const somePlugin2 = getPlugin<{ $somePlugin2Input?: boolean }>(
191-
(props): any => ({
192-
$somePlugin2Input: undefined,
190+
useEffect(() => {
191+
ref.current!.classList.add('some-class-from-hook-ref');
192+
}, []);
193193

194-
ref: node => {
195-
node?.classList.add('some-class-from-functional-ref');
196-
},
197-
}),
198-
);
194+
return {
195+
$somePlugin1Input: undefined,
196+
ref,
197+
};
198+
});
199+
200+
const somePlugin2 = getPlugin<{ $somePlugin2Input?: boolean }>(() => ({
201+
$somePlugin2Input: undefined,
202+
203+
ref: node => {
204+
node?.classList.add('some-class-from-functional-ref');
205+
},
206+
}));
199207

200208
const Div = getElementComponent('div', somePlugin2, somePlugin1);
201209

packages/element-component/src/element-component.tsx

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,37 +32,29 @@ export function getElementComponent<TagName extends TagNames>(
3232
.map(withMappedRef)
3333
.map(withMergeOutputOverInput);
3434

35-
return forwardRef((unprocessedProps: any, ref) => {
35+
return forwardRef((unprocessedProps, ref) => {
3636
const { $$ref = [], ...processedProps }: any = fastPipeline(
3737
unprocessedProps,
3838
...processedPlugins,
3939
) as JSX.IntrinsicElements[TagName];
40-
40+
const callbackRef = React.useCallback(
41+
(node: HTMLElement) => [ref, ...$$ref].forEach(handleRefFor(node)),
42+
$$ref,
43+
);
4144
const TagName = tagName as React.ElementType;
4245

43-
return (
44-
<TagName
45-
{...processedProps}
46-
ref={(node: HTMLElement) => {
47-
const handleRef = handleRefFor(node);
48-
49-
if (ref) {
50-
handleRef(ref);
51-
}
52-
53-
$$ref.forEach(handleRef);
54-
}}
55-
/>
56-
);
46+
return <TagName {...processedProps} ref={callbackRef} />;
5747
});
5848
}
5949

6050
const handleRefFor =
6151
(node: HTMLElement) =>
62-
(ref: ((node: HTMLElement) => void) | MutableRefObject<unknown>) => {
52+
(
53+
ref: ((node: HTMLElement) => void) | MutableRefObject<unknown> | undefined,
54+
) => {
6355
if (typeof ref === 'function') {
6456
ref(node);
65-
} else {
57+
} else if (ref) {
6658
ref.current = node;
6759
}
6860
};

0 commit comments

Comments
 (0)