Skip to content

Commit a69cec8

Browse files
committedMay 1, 2023
perf: close ChatGPTNextWeb#909 reduce message items render time
1 parent 8f5c289 commit a69cec8

File tree

2 files changed

+61
-57
lines changed

2 files changed

+61
-57
lines changed
 

‎app/components/chat.tsx

+4-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useDebouncedCallback } from "use-debounce";
2-
import { memo, useState, useRef, useEffect, useLayoutEffect } from "react";
2+
import { useState, useRef, useEffect, useLayoutEffect } from "react";
33

44
import SendWhiteIcon from "../icons/send-white.svg";
55
import BrainIcon from "../icons/brain.svg";
@@ -64,12 +64,9 @@ import {
6464
useMaskStore,
6565
} from "../store/mask";
6666

67-
const Markdown = dynamic(
68-
async () => memo((await import("./markdown")).Markdown),
69-
{
70-
loading: () => <LoadingIcon />,
71-
},
72-
);
67+
const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
68+
loading: () => <LoadingIcon />,
69+
});
7370

7471
function exportMessages(messages: Message[], topic: string) {
7572
const mdText =

‎app/components/markdown.tsx

+57-50
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { useRef, useState, RefObject, useEffect } from "react";
99
import { copyToClipboard } from "../utils";
1010

1111
import LoadingIcon from "../icons/three-dots.svg";
12+
import React from "react";
1213

1314
export function PreCode(props: { children: any }) {
1415
const ref = useRef<HTMLPreElement>(null);
@@ -29,6 +30,32 @@ export function PreCode(props: { children: any }) {
2930
);
3031
}
3132

33+
function _MarkDownContent(props: { content: string }) {
34+
return (
35+
<ReactMarkdown
36+
remarkPlugins={[RemarkMath, RemarkGfm, RemarkBreaks]}
37+
rehypePlugins={[
38+
RehypeKatex,
39+
[
40+
RehypeHighlight,
41+
{
42+
detect: false,
43+
ignoreMissing: true,
44+
},
45+
],
46+
]}
47+
components={{
48+
pre: PreCode,
49+
}}
50+
linkTarget={"_blank"}
51+
>
52+
{props.content}
53+
</ReactMarkdown>
54+
);
55+
}
56+
57+
export const MarkdownContent = React.memo(_MarkDownContent);
58+
3259
export function Markdown(
3360
props: {
3461
content: string;
@@ -38,69 +65,49 @@ export function Markdown(
3865
} & React.DOMAttributes<HTMLDivElement>,
3966
) {
4067
const mdRef = useRef<HTMLDivElement>(null);
68+
const renderedHeight = useRef(0);
69+
const inView = useRef(false);
4170

4271
const parent = props.parentRef.current;
4372
const md = mdRef.current;
44-
const rendered = useRef(true); // disable lazy loading for bad ux
45-
const [counter, setCounter] = useState(0);
46-
47-
useEffect(() => {
48-
// to triggr rerender
49-
setCounter(counter + 1);
50-
// eslint-disable-next-line react-hooks/exhaustive-deps
51-
}, [props.loading]);
52-
53-
const inView =
54-
rendered.current ||
55-
(() => {
56-
if (parent && md) {
57-
const parentBounds = parent.getBoundingClientRect();
58-
const mdBounds = md.getBoundingClientRect();
59-
const isInRange = (x: number) =>
60-
x <= parentBounds.bottom && x >= parentBounds.top;
61-
const inView = isInRange(mdBounds.top) || isInRange(mdBounds.bottom);
62-
63-
if (inView) {
64-
rendered.current = true;
65-
}
6673

67-
return inView;
68-
}
69-
})();
74+
if (parent && md) {
75+
const parentBounds = parent.getBoundingClientRect();
76+
const twoScreenHeight = Math.max(500, parentBounds.height * 2);
77+
const mdBounds = md.getBoundingClientRect();
78+
const isInRange = (x: number) =>
79+
x <= parentBounds.bottom + twoScreenHeight &&
80+
x >= parentBounds.top - twoScreenHeight;
81+
inView.current = isInRange(mdBounds.top) || isInRange(mdBounds.bottom);
82+
}
7083

71-
const shouldLoading = props.loading || !inView;
84+
if (inView.current && md) {
85+
renderedHeight.current = Math.max(
86+
renderedHeight.current,
87+
md.getBoundingClientRect().height,
88+
);
89+
}
7290

7391
return (
7492
<div
7593
className="markdown-body"
76-
style={{ fontSize: `${props.fontSize ?? 14}px` }}
94+
style={{
95+
fontSize: `${props.fontSize ?? 14}px`,
96+
height:
97+
!inView.current && renderedHeight.current > 0
98+
? renderedHeight.current
99+
: "auto",
100+
}}
77101
ref={mdRef}
78102
onContextMenu={props.onContextMenu}
79103
onDoubleClickCapture={props.onDoubleClickCapture}
80104
>
81-
{shouldLoading ? (
82-
<LoadingIcon />
83-
) : (
84-
<ReactMarkdown
85-
remarkPlugins={[RemarkMath, RemarkGfm, RemarkBreaks]}
86-
rehypePlugins={[
87-
RehypeKatex,
88-
[
89-
RehypeHighlight,
90-
{
91-
detect: false,
92-
ignoreMissing: true,
93-
},
94-
],
95-
]}
96-
components={{
97-
pre: PreCode,
98-
}}
99-
linkTarget={"_blank"}
100-
>
101-
{props.content}
102-
</ReactMarkdown>
103-
)}
105+
{inView.current &&
106+
(props.loading ? (
107+
<LoadingIcon />
108+
) : (
109+
<MarkdownContent content={props.content} />
110+
))}
104111
</div>
105112
);
106113
}

0 commit comments

Comments
 (0)
Please sign in to comment.