Follow-up from #9588, which fixed 26 of the 27 react-hooks 7 sites. This is the last one.
Problem
DocsToc (apps/loopover-ui/src/components/site/docs-toc.tsx) builds the "On this page" rail by querying the rendered DOM for h2, h3 inside article.prose-docs, assigning ids to any heading that lacks one, and reading the result into state from an effect.
That is the one remaining react-hooks/set-state-in-effect site in the app, and it is the reason the rule is still at warn rather than error. It is also fragile independently of lint: the TOC depends on the rendered markup rather than on the content, so a styling change to the article wrapper silently empties the rail.
The real fix
fumadocs compiles a TOC already — CompiledMDXProperties.toc: TOCItemType[] (node_modules/fumadocs-mdx/dist/core-BmvJhZxm.d.ts). Sourcing the rail from that removes the DOM scrape, the id back-filling, and the effect together.
Why it is not a one-liner
Two things have to be resolved first:
- The data and the component are in different routes.
DocsToc renders in the docs LAYOUT (src/routes/docs.tsx), while the compiled page — and therefore its toc — is resolved in the child route (src/routes/docs.$slug.tsx). The toc has to reach the layout, via route context or by moving the rail into the child.
- Not every docs page is MDX.
docs.index.tsx and docs.fumadocs-spike-api-reference.tsx render under the same layout and have no compiled toc. They get a rail today from the DOM scrape, so a naive switch silently removes it for them.
Acceptance
The rail is built from content rather than from rendered markup for MDX pages, non-MDX docs pages keep a working rail (or deliberately opt out), and react-hooks/set-state-in-effect goes back to error in apps/loopover-ui/eslint.config.ts with no remaining suppressions.
Follow-up from #9588, which fixed 26 of the 27 react-hooks 7 sites. This is the last one.
Problem
DocsToc(apps/loopover-ui/src/components/site/docs-toc.tsx) builds the "On this page" rail by querying the rendered DOM forh2, h3insidearticle.prose-docs, assigning ids to any heading that lacks one, and reading the result into state from an effect.That is the one remaining
react-hooks/set-state-in-effectsite in the app, and it is the reason the rule is still atwarnrather thanerror. It is also fragile independently of lint: the TOC depends on the rendered markup rather than on the content, so a styling change to the article wrapper silently empties the rail.The real fix
fumadocs compiles a TOC already —
CompiledMDXProperties.toc: TOCItemType[](node_modules/fumadocs-mdx/dist/core-BmvJhZxm.d.ts). Sourcing the rail from that removes the DOM scrape, the id back-filling, and the effect together.Why it is not a one-liner
Two things have to be resolved first:
DocsTocrenders in the docs LAYOUT (src/routes/docs.tsx), while the compiled page — and therefore itstoc— is resolved in the child route (src/routes/docs.$slug.tsx). The toc has to reach the layout, via route context or by moving the rail into the child.docs.index.tsxanddocs.fumadocs-spike-api-reference.tsxrender under the same layout and have no compiled toc. They get a rail today from the DOM scrape, so a naive switch silently removes it for them.Acceptance
The rail is built from content rather than from rendered markup for MDX pages, non-MDX docs pages keep a working rail (or deliberately opt out), and
react-hooks/set-state-in-effectgoes back toerrorinapps/loopover-ui/eslint.config.tswith no remaining suppressions.