mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-01 09:49:03 +08:00
perf: part of memory leak (#8619)
* fix: 修复多个组件中的内存泄漏问题 清理setTimeout和事件监听器以避免内存泄漏 优化useEffect中的异步操作清理逻辑 * fix: review comments
This commit is contained in:
parent
27af64f2bd
commit
b716a7446a
@ -16,12 +16,22 @@ const EmojiPicker: FC<Props> = ({ onEmojiClick }) => {
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (ref.current) {
|
||||
ref.current.addEventListener('emoji-click', (event: any) => {
|
||||
const refValue = ref.current
|
||||
|
||||
if (refValue) {
|
||||
const handleEmojiClick = (event: any) => {
|
||||
event.stopPropagation()
|
||||
onEmojiClick(event.detail.unicode || event.detail.emoji.unicode)
|
||||
})
|
||||
}
|
||||
// 添加事件监听器
|
||||
refValue.addEventListener('emoji-click', handleEmojiClick)
|
||||
|
||||
// 清理事件监听器
|
||||
return () => {
|
||||
refValue.removeEventListener('emoji-click', handleEmojiClick)
|
||||
}
|
||||
}
|
||||
return
|
||||
}, [onEmojiClick])
|
||||
|
||||
// @ts-ignore next-line
|
||||
|
||||
@ -64,9 +64,9 @@ const WebviewContainer = memo(
|
||||
webviewRef.current.src = url
|
||||
|
||||
return () => {
|
||||
webviewRef.current?.removeEventListener('dom-ready', handleDomReady)
|
||||
webviewRef.current?.removeEventListener('did-finish-load', handleLoaded)
|
||||
webviewRef.current?.removeEventListener('did-navigate-in-page', handleNavigate)
|
||||
webviewRef.current?.removeEventListener('dom-ready', handleDomReady)
|
||||
}
|
||||
// because the appid and url are enough, no need to add onLoadedCallback
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
||||
@ -162,6 +162,9 @@ const PopupContainer: React.FC<Props> = ({ provider: _provider, resolve }) => {
|
||||
const onRemoveModel = useCallback((model: Model) => removeModel(model), [removeModel])
|
||||
|
||||
useEffect(() => {
|
||||
let timer: NodeJS.Timeout
|
||||
let mounted = true
|
||||
|
||||
runAsyncFunction(async () => {
|
||||
try {
|
||||
setLoading(true)
|
||||
@ -188,18 +191,32 @@ const PopupContainer: React.FC<Props> = ({ provider: _provider, resolve }) => {
|
||||
} catch (error) {
|
||||
logger.error('Failed to fetch models', error as Error)
|
||||
} finally {
|
||||
setTimeout(() => setLoading(false), 300)
|
||||
if (mounted) {
|
||||
timer = setTimeout(() => setLoading(false), 300)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return () => {
|
||||
mounted = false
|
||||
if (timer) {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (open && searchInputRef.current) {
|
||||
setTimeout(() => {
|
||||
const timer = setTimeout(() => {
|
||||
searchInputRef.current?.focus()
|
||||
}, 350)
|
||||
|
||||
return () => {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
}
|
||||
return
|
||||
}, [open])
|
||||
|
||||
const ModalHeader = () => {
|
||||
|
||||
@ -141,7 +141,10 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
open && setTimeout(() => inputRef.current?.focus(), 0)
|
||||
if (!open) return
|
||||
|
||||
const timer = setTimeout(() => inputRef.current?.focus(), 0)
|
||||
return () => clearTimeout(timer)
|
||||
}, [open])
|
||||
|
||||
return (
|
||||
|
||||
@ -346,7 +346,8 @@ const PopupContainer: React.FC<Props> = ({ model, resolve, modelFilter }) => {
|
||||
// 初始化焦点和滚动位置
|
||||
useEffect(() => {
|
||||
if (!open) return
|
||||
setTimeout(() => inputRef.current?.focus(), 0)
|
||||
const timer = setTimeout(() => inputRef.current?.focus(), 0)
|
||||
return () => clearTimeout(timer)
|
||||
}, [open])
|
||||
|
||||
const togglePin = useCallback(
|
||||
|
||||
@ -76,7 +76,8 @@ const PopupContainer: React.FC<Props> = ({
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(resizeTextArea, 0)
|
||||
const timer = setTimeout(resizeTextArea, 0)
|
||||
return () => clearTimeout(timer)
|
||||
}, [])
|
||||
|
||||
const handleAfterOpenChange = (visible: boolean) => {
|
||||
|
||||
@ -51,11 +51,15 @@ const Selector = <V extends string | number>({
|
||||
const inputRef = useRef<any>(null)
|
||||
|
||||
useEffect(() => {
|
||||
let timer: NodeJS.Timeout
|
||||
if (open) {
|
||||
setTimeout(() => {
|
||||
timer = setTimeout(() => {
|
||||
inputRef.current?.focus()
|
||||
}, 1)
|
||||
}
|
||||
return () => {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
}, [open])
|
||||
|
||||
const selectedValues = useMemo(() => {
|
||||
|
||||
@ -22,7 +22,8 @@ export const TopNavbarOpenedMinappTabs: FC = () => {
|
||||
const [keepAliveMinapps, setKeepAliveMinapps] = useState(openedKeepAliveMinapps)
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => setKeepAliveMinapps(openedKeepAliveMinapps), 300)
|
||||
const timer = setTimeout(() => setKeepAliveMinapps(openedKeepAliveMinapps), 300)
|
||||
return () => clearTimeout(timer)
|
||||
}, [openedKeepAliveMinapps])
|
||||
|
||||
const handleOnClick = (app) => {
|
||||
|
||||
@ -709,7 +709,8 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
|
||||
}, [assistant, topic])
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => resizeTextArea(), 0)
|
||||
const timerId = requestAnimationFrame(() => resizeTextArea())
|
||||
return () => cancelAnimationFrame(timerId)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
|
||||
@ -31,6 +31,7 @@ const WebSearchButton: FC<Props> = ({ ref, assistant, ToolbarButton }) => {
|
||||
const updateSelectedWebSearchProvider = useCallback(
|
||||
(providerId?: WebSearchProvider['id']) => {
|
||||
// TODO: updateAssistant有性能问题,会导致关闭快捷面板卡顿
|
||||
// NOTE: 也许可以用startTransition优化卡顿问题
|
||||
setTimeout(() => {
|
||||
const currentWebSearchProviderId = assistant.webSearchProviderId
|
||||
const newWebSearchProviderId = currentWebSearchProviderId === providerId ? undefined : providerId
|
||||
|
||||
@ -446,12 +446,16 @@ const ChatFlowHistory: FC<ChatFlowHistoryProps> = ({ conversationId }) => {
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true)
|
||||
setTimeout(() => {
|
||||
const timer = setTimeout(() => {
|
||||
const { nodes: flowNodes, edges: flowEdges } = buildConversationFlowData()
|
||||
setNodes([...flowNodes])
|
||||
setEdges([...flowEdges])
|
||||
setLoading(false)
|
||||
}, 500)
|
||||
|
||||
return () => {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
}, [buildConversationFlowData, setNodes, setEdges])
|
||||
|
||||
return (
|
||||
|
||||
@ -97,11 +97,13 @@ const MessageBlockEditor: FC<Props> = ({ message, topicId, onSave, onResend, onC
|
||||
}, [couldAddImageFile, couldAddTextFile])
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
const timer = setTimeout(() => {
|
||||
if (textareaRef.current) {
|
||||
textareaRef.current.focus({ cursor: 'end' })
|
||||
}
|
||||
}, 0)
|
||||
|
||||
return () => clearTimeout(timer)
|
||||
}, [])
|
||||
|
||||
// 仅在打开时执行一次
|
||||
|
||||
@ -494,12 +494,18 @@ const CollapsedContent: FC<{ isExpanded: boolean; resultString: string }> = ({ i
|
||||
const [styledResult, setStyledResult] = useState<string>('')
|
||||
|
||||
useEffect(() => {
|
||||
if (!isExpanded) {
|
||||
return
|
||||
}
|
||||
|
||||
const highlight = async () => {
|
||||
const result = await highlightCode(isExpanded ? resultString : '', 'json')
|
||||
const result = await highlightCode(resultString, 'json')
|
||||
setStyledResult(result)
|
||||
}
|
||||
|
||||
setTimeout(highlight, 0)
|
||||
const timer = setTimeout(highlight, 0)
|
||||
|
||||
return () => clearTimeout(timer)
|
||||
}, [isExpanded, resultString, highlightCode])
|
||||
|
||||
if (!isExpanded) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user