From a69cec89fb3b4264abaaa9537c5351bbe7860882 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Tue, 2 May 2023 00:31:44 +0800 Subject: [PATCH] perf: close #909 reduce message items render time --- app/components/chat.tsx | 11 ++-- app/components/markdown.tsx | 107 +++++++++++++++++++----------------- 2 files changed, 61 insertions(+), 57 deletions(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 4c168669..bd2c913d 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -1,5 +1,5 @@ import { useDebouncedCallback } from "use-debounce"; -import { memo, useState, useRef, useEffect, useLayoutEffect } from "react"; +import { useState, useRef, useEffect, useLayoutEffect } from "react"; import SendWhiteIcon from "../icons/send-white.svg"; import BrainIcon from "../icons/brain.svg"; @@ -64,12 +64,9 @@ import { useMaskStore, } from "../store/mask"; -const Markdown = dynamic( - async () => memo((await import("./markdown")).Markdown), - { - loading: () => , - }, -); +const Markdown = dynamic(async () => (await import("./markdown")).Markdown, { + loading: () => , +}); function exportMessages(messages: Message[], topic: string) { const mdText = diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index 25d0584f..2909293c 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -9,6 +9,7 @@ import { useRef, useState, RefObject, useEffect } from "react"; import { copyToClipboard } from "../utils"; import LoadingIcon from "../icons/three-dots.svg"; +import React from "react"; export function PreCode(props: { children: any }) { const ref = useRef(null); @@ -29,6 +30,32 @@ export function PreCode(props: { children: any }) { ); } +function _MarkDownContent(props: { content: string }) { + return ( + + {props.content} + + ); +} + +export const MarkdownContent = React.memo(_MarkDownContent); + export function Markdown( props: { content: string; @@ -38,69 +65,49 @@ export function Markdown( } & React.DOMAttributes, ) { const mdRef = useRef(null); + const renderedHeight = useRef(0); + const inView = useRef(false); const parent = props.parentRef.current; const md = mdRef.current; - const rendered = useRef(true); // disable lazy loading for bad ux - const [counter, setCounter] = useState(0); - useEffect(() => { - // to triggr rerender - setCounter(counter + 1); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.loading]); + if (parent && md) { + const parentBounds = parent.getBoundingClientRect(); + const twoScreenHeight = Math.max(500, parentBounds.height * 2); + const mdBounds = md.getBoundingClientRect(); + const isInRange = (x: number) => + x <= parentBounds.bottom + twoScreenHeight && + x >= parentBounds.top - twoScreenHeight; + inView.current = isInRange(mdBounds.top) || isInRange(mdBounds.bottom); + } - const inView = - rendered.current || - (() => { - if (parent && md) { - const parentBounds = parent.getBoundingClientRect(); - const mdBounds = md.getBoundingClientRect(); - const isInRange = (x: number) => - x <= parentBounds.bottom && x >= parentBounds.top; - const inView = isInRange(mdBounds.top) || isInRange(mdBounds.bottom); - - if (inView) { - rendered.current = true; - } - - return inView; - } - })(); - - const shouldLoading = props.loading || !inView; + if (inView.current && md) { + renderedHeight.current = Math.max( + renderedHeight.current, + md.getBoundingClientRect().height, + ); + } return (
0 + ? renderedHeight.current + : "auto", + }} ref={mdRef} onContextMenu={props.onContextMenu} onDoubleClickCapture={props.onDoubleClickCapture} > - {shouldLoading ? ( - - ) : ( - - {props.content} - - )} + {inView.current && + (props.loading ? ( + + ) : ( + + ))}
); }