Skip to content

Commit ce7bd5f

Browse files
committed
Get mutation observer working
1 parent 34defa7 commit ce7bd5f

File tree

1 file changed

+46
-25
lines changed

1 file changed

+46
-25
lines changed

frontend/src/routes/_account.plan.index.tsx

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import { queryOptions, useSuspenseQuery } from "@tanstack/react-query";
77
import { Navigate, createFileRoute, redirect } from "@tanstack/react-router";
8-
import { type Ref, useCallback } from "react";
8+
import { useEffect, useMemo, useRef } from "react";
99
import { preload } from "react-dom";
1010
import { graphql } from "../gql";
1111
import { graphqlRequest } from "../graphql";
@@ -46,9 +46,15 @@ function Plan(): React.ReactElement {
4646
return <Navigate to="/" replace />;
4747
}
4848

49+
const ref = useRef<HTMLIFrameElement>(null);
50+
4951
// Query the size of the iframe content and set the height
5052
// This will only work where the iframe is served from the same origin
51-
const calculateHeight = useCallback((iframe: HTMLIFrameElement) => {
53+
const calculateHeight = () => {
54+
const iframe = ref.current;
55+
if (!iframe) {
56+
return;
57+
}
5258
const height =
5359
iframe.contentWindow?.document.body.parentElement?.scrollHeight;
5460

@@ -57,37 +63,52 @@ function Plan(): React.ReactElement {
5763
} else {
5864
iframe.height = "500px";
5965
}
60-
}, []);
61-
62-
const ref: Ref<HTMLIFrameElement> = useCallback(
63-
(iframe: HTMLIFrameElement | null) => {
64-
if (!iframe) return;
65-
calculateHeight(iframe);
66-
67-
if (iframe.contentWindow) {
68-
const iframeDocument = iframe.contentWindow.document;
66+
};
6967

70-
const observer = new MutationObserver((_mutationsList) => {
71-
calculateHeight(iframe);
72-
});
68+
const observer = useMemo(
69+
() =>
70+
new MutationObserver((_mutationsList) => {
71+
// we calculate the height immediately when the observer is triggered
72+
calculateHeight();
73+
// then we recalculate the height after a short timeout
74+
// to ensure that any layout changes have settled
75+
setTimeout(() => {
76+
calculateHeight();
77+
}, 1000);
78+
// n.b. we don't worry about the timeout happening after the component is unmounted
79+
}),
80+
[],
81+
);
7382

74-
observer.observe(iframeDocument.body, {
75-
childList: true,
76-
subtree: true,
77-
attributes: true,
78-
});
83+
useEffect(() => {
84+
const iframe = ref.current;
85+
if (iframe) {
86+
attachObserver(iframe);
87+
}
88+
// Cleanup observer when the component unmounts
89+
return () => observer.disconnect();
90+
}, []);
7991

80-
return () => observer.disconnect();
81-
}
82-
},
83-
[calculateHeight],
84-
);
92+
const attachObserver = (iframe: HTMLIFrameElement) => {
93+
const iframeBody = iframe.contentWindow?.document.body;
94+
if (!iframeBody) {
95+
return;
96+
}
97+
// calculate the height immediately
98+
calculateHeight();
99+
// observe future changes to the body of the iframe
100+
observer.observe(iframeBody, {
101+
childList: true,
102+
subtree: true,
103+
attributes: true,
104+
});
105+
};
85106

86107
return (
87108
<iframe
88109
title="iframe" // no proper title as this is experimental feature
89110
ref={ref}
90-
onLoad={(e) => calculateHeight(e.target as HTMLIFrameElement)}
111+
onLoad={(e) => attachObserver(e.target as HTMLIFrameElement)}
91112
src={planManagementIframeUri}
92113
scrolling="no"
93114
/>

0 commit comments

Comments
 (0)