From a948bf6ee8ee5f39a569ee88b21791cc13d854a5 Mon Sep 17 00:00:00 2001 From: Xingyu <34761674+xy8@users.noreply.github.com> Date: Tue, 2 Jul 2024 06:26:56 +0000 Subject: [PATCH] fix: react.js error 185 maximum update depth exceeded in streaming responses during conversation (#5849) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 林星宇 --- web/app/components/base/markdown.tsx | 127 ++++++++++++++++----------- 1 file changed, 74 insertions(+), 53 deletions(-) diff --git a/web/app/components/base/markdown.tsx b/web/app/components/base/markdown.tsx index 14a630b2ed..958c55a871 100644 --- a/web/app/components/base/markdown.tsx +++ b/web/app/components/base/markdown.tsx @@ -7,8 +7,9 @@ import RemarkGfm from 'remark-gfm' import SyntaxHighlighter from 'react-syntax-highlighter' import { atelierHeathLight } from 'react-syntax-highlighter/dist/esm/styles/hljs' import type { RefObject } from 'react' -import { useEffect, useRef, useState } from 'react' +import { memo, useEffect, useMemo, useRef, useState } from 'react' import cn from 'classnames' +import type { CodeComponent } from 'react-markdown/lib/ast-to-react' import CopyBtn from '@/app/components/base/copy-btn' import SVGBtn from '@/app/components/base/svg' import Flowchart from '@/app/components/base/mermaid' @@ -89,8 +90,78 @@ const useLazyLoad = (ref: RefObject): boolean => { return isIntersecting } -export function Markdown(props: { content: string; className?: string }) { +// **Add code block +// Avoid error #185 (Maximum update depth exceeded. +// This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. +// React limits the number of nested updates to prevent infinite loops.) +// Reference A: https://reactjs.org/docs/error-decoder.html?invariant=185 +// Reference B1: https://react.dev/reference/react/memo +// Reference B2: https://react.dev/reference/react/useMemo +// **** +// The original error that occurred in the streaming response during the conversation: +// Error: Minified React error 185; +// visit https://reactjs.org/docs/error-decoder.html?invariant=185 for the full message +// or use the non-minified dev environment for full errors and additional helpful warnings. +const CodeBlock: CodeComponent = memo(({ inline, className, children, ...props }) => { const [isSVG, setIsSVG] = useState(false) + const match = /language-(\w+)/.exec(className || '') + const language = match?.[1] + const languageShowName = getCorrectCapitalizationLanguageName(language || '') + + // Use `useMemo` to ensure that `SyntaxHighlighter` only re-renders when necessary + return useMemo(() => { + return (!inline && match) + ? ( +
+
+
{languageShowName}
+
+ {language === 'mermaid' + && + } + +
+
+ {(language === 'mermaid' && isSVG) + ? () + : ( + {String(children).replace(/\n$/, '')} + )} +
+ ) + : ( + + {children} + + ) + }, [children, className, inline, isSVG, language, languageShowName, match, props]) +}) + +CodeBlock.displayName = 'CodeBlock' + +export function Markdown(props: { content: string; className?: string }) { const latexContent = preprocessLaTeX(props.content) return (
@@ -100,57 +171,7 @@ export function Markdown(props: { content: string; className?: string }) { RehypeKatex as any, ]} components={{ - code({ inline, className, children, ...props }) { - const match = /language-(\w+)/.exec(className || '') - const language = match?.[1] - const languageShowName = getCorrectCapitalizationLanguageName(language || '') - return (!inline && match) - ? ( -
-
-
{languageShowName}
-
- {language === 'mermaid' - && - } - -
-
- {(language === 'mermaid' && isSVG) - ? () - : ( - {String(children).replace(/\n$/, '')} - )} -
- ) - : ( - - {children} - - ) - }, + code: CodeBlock, img({ src, alt, ...props }) { return ( // eslint-disable-next-line @next/next/no-img-element