From 5d4a1f49d642d4716b605cbf6e8387ae41663668 Mon Sep 17 00:00:00 2001 From: one Date: Sat, 16 Aug 2025 18:42:02 +0800 Subject: [PATCH] refactor: improve MarkdownSvgRenderer re-render --- .../home/Markdown/MarkdownSvgRenderer.tsx | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/src/renderer/src/pages/home/Markdown/MarkdownSvgRenderer.tsx b/src/renderer/src/pages/home/Markdown/MarkdownSvgRenderer.tsx index 81840a0ebf..6d51f5a35d 100644 --- a/src/renderer/src/pages/home/Markdown/MarkdownSvgRenderer.tsx +++ b/src/renderer/src/pages/home/Markdown/MarkdownSvgRenderer.tsx @@ -1,5 +1,5 @@ import { makeSvgSizeAdaptive } from '@renderer/utils/image' -import React, { FC, useEffect, useRef, useState } from 'react' +import React, { FC, useEffect, useRef } from 'react' interface SvgProps extends React.SVGProps { 'data-needs-measurement'?: 'true' @@ -11,35 +11,40 @@ interface SvgProps extends React.SVGProps { * This component handles two types of SVGs passed from `react-markdown`: * * 1. **Pre-processed SVGs**: Simple SVGs that were already handled by the - * `rehypeScalableSvg` plugin. These are rendered directly with zero - * performance overhead. + * `rehypeScalableSvg` plugin. These are rendered directly. * - * 2. **SVGs needing measurement**: Complex SVGs (e.g., with unit-based - * dimensions like "100pt") are flagged with `data-needs-measurement`. - * This component will perform a one-time, off-screen measurement for - * these SVGs upon mounting to ensure they are rendered correctly and - * scalably. + * 2. **SVGs needing measurement**: Complex SVGs are flagged with + * `data-needs-measurement`. This component performs a one-time DOM + * mutation upon mounting to make them scalable. To prevent React from + * reverting these changes during subsequent renders, it stops passing + * the original `width` and `height` props after the mutation is complete. */ const MarkdownSvgRenderer: FC = (props) => { const { 'data-needs-measurement': needsMeasurement, ...restProps } = props const svgRef = useRef(null) - const [isMeasured, setIsMeasured] = useState(false) + const isMeasuredRef = useRef(false) useEffect(() => { - if (needsMeasurement && svgRef.current && !isMeasured) { - // The element is a real DOM node, we can now measure it. + if (needsMeasurement && svgRef.current && !isMeasuredRef.current) { + // Directly mutate the DOM element to make it adaptive. makeSvgSizeAdaptive(svgRef.current) - // Set flag to prevent re-measuring on subsequent renders - setIsMeasured(true) + // Set flag to prevent re-measuring. This does not trigger a re-render. + isMeasuredRef.current = true } - }, [needsMeasurement, isMeasured]) + }, [needsMeasurement]) - // For SVGs that need measurement, we render them once with their original - // props to allow the ref to capture the DOM element for measurement. - // The `useEffect` will then trigger, process the element, and cause a - // re-render with the correct scalable attributes. - // For simple SVGs, they are rendered correctly from the start. - return + // Create a mutable copy of props to potentially modify. + const finalProps = { ...restProps } + + // If the SVG has been measured and mutated, we prevent React from + // re-applying the original width and height attributes on subsequent renders. + // This preserves the changes made by `makeSvgSizeAdaptive`. + if (isMeasuredRef.current) { + delete finalProps.width + delete finalProps.height + } + + return } export default MarkdownSvgRenderer