Skip to content

Commit a08454d

Browse files
fix(Popper): prevented forceUpdate from firing when not necessary (#9088)
1 parent c2c8d50 commit a08454d

File tree

1 file changed

+22
-8
lines changed

1 file changed

+22
-8
lines changed

packages/react-core/src/helpers/Popper/Popper.tsx

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ export const Popper: React.FunctionComponent<PopperProps> = ({
181181
const [triggerElement, setTriggerElement] = React.useState(null);
182182
const [refElement, setRefElement] = React.useState<HTMLElement>(null);
183183
const [popperElement, setPopperElement] = React.useState<HTMLElement>(null);
184+
const [popperContent, setPopperContent] = React.useState(null);
184185
const [ready, setReady] = React.useState(false);
185186
const refOrTrigger = refElement || triggerElement;
186187
const onDocumentClickCallback = React.useCallback(
@@ -210,6 +211,18 @@ export const Popper: React.FunctionComponent<PopperProps> = ({
210211
}
211212
}
212213
}, [isVisible, popperRef]);
214+
React.useEffect(() => {
215+
// Trigger a Popper update when content changes.
216+
const observer = new MutationObserver(() => {
217+
update && update();
218+
});
219+
popperElement && observer.observe(popperElement, { attributes: true, childList: true, subtree: true });
220+
221+
return () => {
222+
observer.disconnect();
223+
};
224+
}, [popperElement]);
225+
213226
const addEventListener = (listener: any, element: Document | HTMLElement, event: string, capture = false) => {
214227
if (listener && element) {
215228
element.addEventListener(event, listener, { capture });
@@ -234,12 +247,6 @@ export const Popper: React.FunctionComponent<PopperProps> = ({
234247
onDocumentClick && addEventListener(onDocumentClickCallback, document, 'click', true);
235248
addEventListener(onDocumentKeyDown, document, 'keydown', true);
236249

237-
// Trigger a Popper update when content changes.
238-
const observer = new MutationObserver(() => {
239-
update && update();
240-
});
241-
popperElement && observer.observe(popperElement, { attributes: true, childList: true, subtree: true });
242-
243250
return () => {
244251
removeEventListener(onMouseEnter, refOrTrigger, 'mouseenter');
245252
removeEventListener(onMouseLeave, refOrTrigger, 'mouseleave');
@@ -252,7 +259,6 @@ export const Popper: React.FunctionComponent<PopperProps> = ({
252259
removeEventListener(onPopperMouseLeave, popperElement, 'mouseleave');
253260
onDocumentClick && removeEventListener(onDocumentClickCallback, document, 'click', true);
254261
removeEventListener(onDocumentKeyDown, document, 'keydown', true);
255-
observer.disconnect();
256262
};
257263
}, [
258264
triggerElement,
@@ -359,8 +365,16 @@ export const Popper: React.FunctionComponent<PopperProps> = ({
359365
]
360366
});
361367

368+
/** We want to forceUpdate only when a tooltip's content is dynamically updated.
369+
* TODO: Investigate into 3rd party libraries for a less limited/specific solution
370+
*/
362371
React.useEffect(() => {
363-
forceUpdate && forceUpdate();
372+
const currentPopperContent = popper?.props?.children[1]?.props?.children;
373+
setPopperContent(currentPopperContent);
374+
375+
if (currentPopperContent && popperContent && currentPopperContent !== popperContent) {
376+
forceUpdate && forceUpdate();
377+
}
364378
}, [popper]);
365379

366380
// Returns the CSS modifier class in order to place the Popper's arrow properly

0 commit comments

Comments
 (0)