diff --git a/site/components/Heading.js b/site/components/Heading.js index 65cfde0..8cbaa4a 100644 --- a/site/components/Heading.js +++ b/site/components/Heading.js @@ -5,13 +5,10 @@ export const Heading = ({ level, observer }) => (props) => { useEffect(() => { /* start observing heading's intersection with the bounding box * set by observer's `rootMargin` */ - if (observer) { - observer.observe(document.getElementById(props.id)); - - return () => { - observer.unobserve(document.getElementById(props.id)); - } + if (!observer) { + return } + observer.observe(document.getElementById(props.id)); }); return React.createElement(`h${level}`, { diff --git a/site/hooks/useHeadingsObserver.js b/site/hooks/useHeadingsObserver.js index 3df4acc..df5ac8b 100644 --- a/site/hooks/useHeadingsObserver.js +++ b/site/hooks/useHeadingsObserver.js @@ -4,28 +4,35 @@ import { useState, useEffect } from 'react'; /* Creates an Intersection Observer to keep track of headings intersecting * a section of the viewport defined by the rootMargin */ const getIntersectionObserver = (callback) => { - if (typeof window !== 'undefined') { - return new IntersectionObserver( - (entries) => { - entries.forEach((entry) => { - callback(entry); - }); - }, - { - root: null, - rootMargin: "-65px 0% -85% 0%" // 65px is a navbar height - } - ); - } + return new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + callback(entry); + }); + }, + { + root: null, + rootMargin: "-65px 0% -85% 0%" // 65px is a navbar height + } + ); }; const useHeadingsObserver = () => { const [activeHeading, setActiveHeading] = useState(""); - const [observer, setObserver] = useState(getIntersectionObserver((entry) => { + const [observer, setObserver] = useState(null); + + /* Runs only after the first render, in order to preserve the observer + * between component rerenderings (e.g. after activeHeading change). */ + useEffect((() => { + const observer = getIntersectionObserver((entry) => { if (entry.isIntersecting) { setActiveHeading(entry.target.id); } - })); + }); + setObserver(observer); + + return observer.disconnect(); + }), []) /* On initial render activeHeading will be `null`, since the observer * has not been instantiated yet. However, we still want to highlight