perf: optimize marketplace card re-renders with memoization (#29263)

This commit is contained in:
yyh 2025-12-08 14:09:03 +08:00 committed by GitHub
parent 6942666d03
commit 2f96374837
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 41 additions and 11 deletions

View File

@ -1,3 +1,4 @@
import React from 'react'
import { RiInstallLine } from '@remixicon/react'
import { formatNumber } from '@/utils/format'
@ -5,7 +6,7 @@ type Props = {
downloadCount: number
}
const DownloadCount = ({
const DownloadCountComponent = ({
downloadCount,
}: Props) => {
return (
@ -16,4 +17,7 @@ const DownloadCount = ({
)
}
// Memoize to prevent unnecessary re-renders
const DownloadCount = React.memo(DownloadCountComponent)
export default DownloadCount

View File

@ -1,3 +1,4 @@
import React from 'react'
import DownloadCount from './base/download-count'
type Props = {
@ -5,7 +6,7 @@ type Props = {
tags: string[]
}
const CardMoreInfo = ({
const CardMoreInfoComponent = ({
downloadCount,
tags,
}: Props) => {
@ -33,4 +34,7 @@ const CardMoreInfo = ({
)
}
// Memoize to prevent unnecessary re-renders when tags array hasn't changed
const CardMoreInfo = React.memo(CardMoreInfoComponent)
export default CardMoreInfo

View File

@ -1,4 +1,5 @@
'use client'
import React, { useMemo } from 'react'
import { useTheme } from 'next-themes'
import { RiArrowRightUpLine } from '@remixicon/react'
import { getPluginDetailLinkInMarketplace, getPluginLinkInMarketplace } from '../utils'
@ -17,7 +18,7 @@ type CardWrapperProps = {
showInstallButton?: boolean
locale?: string
}
const CardWrapper = ({
const CardWrapperComponent = ({
plugin,
showInstallButton,
locale,
@ -31,6 +32,18 @@ const CardWrapper = ({
const { locale: localeFromLocale } = useI18N()
const { getTagLabel } = useTags(t)
// Memoize marketplace link params to prevent unnecessary re-renders
const marketplaceLinkParams = useMemo(() => ({
language: localeFromLocale,
theme,
}), [localeFromLocale, theme])
// Memoize tag labels to prevent recreating array on every render
const tagLabels = useMemo(() =>
plugin.tags.map(tag => getTagLabel(tag.name)),
[plugin.tags, getTagLabel],
)
if (showInstallButton) {
return (
<div
@ -43,12 +56,12 @@ const CardWrapper = ({
footer={
<CardMoreInfo
downloadCount={plugin.install_count}
tags={plugin.tags.map(tag => getTagLabel(tag.name))}
tags={tagLabels}
/>
}
/>
{
<div className='absolute bottom-0 hidden w-full items-center space-x-2 rounded-b-xl bg-gradient-to-tr from-components-panel-on-panel-item-bg to-background-gradient-mask-transparent px-4 pb-4 pt-8 group-hover:flex'>
<div className='absolute bottom-0 hidden w-full items-center space-x-2 rounded-b-xl bg-gradient-to-tr from-components-panel-on-panel-item-bg to-background-gradient-mask-transparent px-4 pb-4 pt-4 group-hover:flex'>
<Button
variant='primary'
className='w-[calc(50%-4px)]'
@ -56,7 +69,7 @@ const CardWrapper = ({
>
{t('plugin.detailPanel.operation.install')}
</Button>
<a href={getPluginLinkInMarketplace(plugin, { language: localeFromLocale, theme })} target='_blank' className='block w-[calc(50%-4px)] flex-1 shrink-0'>
<a href={getPluginLinkInMarketplace(plugin, marketplaceLinkParams)} target='_blank' className='block w-[calc(50%-4px)] flex-1 shrink-0'>
<Button
className='w-full gap-0.5'
>
@ -92,7 +105,7 @@ const CardWrapper = ({
footer={
<CardMoreInfo
downloadCount={plugin.install_count}
tags={plugin.tags.map(tag => getTagLabel(tag.name))}
tags={tagLabels}
/>
}
/>
@ -100,4 +113,7 @@ const CardWrapper = ({
)
}
// Memoize the component to prevent unnecessary re-renders when props haven't changed
const CardWrapper = React.memo(CardWrapperComponent)
export default CardWrapper

View File

@ -1,5 +1,5 @@
'use client'
import React from 'react'
import React, { useMemo } from 'react'
import type { FC } from 'react'
import { useTheme } from 'next-themes'
import { useTranslation } from 'react-i18next'
@ -23,7 +23,7 @@ type Props = {
payload: Plugin
}
const ProviderCard: FC<Props> = ({
const ProviderCardComponent: FC<Props> = ({
className,
payload,
}) => {
@ -37,6 +37,9 @@ const ProviderCard: FC<Props> = ({
const { org, label } = payload
const { locale } = useI18N()
// Memoize the marketplace link params to prevent unnecessary re-renders
const marketplaceLinkParams = useMemo(() => ({ language: locale, theme }), [locale, theme])
return (
<div className={cn('group relative rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg p-4 pb-3 shadow-xs hover:bg-components-panel-on-panel-item-bg', className)}>
{/* Header */}
@ -63,7 +66,7 @@ const ProviderCard: FC<Props> = ({
))}
</div>
<div
className='absolute bottom-0 left-0 right-0 hidden items-center gap-2 rounded-xl bg-gradient-to-tr from-components-panel-on-panel-item-bg to-background-gradient-mask-transparent p-4 pt-8 group-hover:flex'
className='absolute bottom-0 left-0 right-0 hidden items-center gap-2 rounded-xl bg-gradient-to-tr from-components-panel-on-panel-item-bg to-background-gradient-mask-transparent p-4 pt-4 group-hover:flex'
>
<Button
className='grow'
@ -76,7 +79,7 @@ const ProviderCard: FC<Props> = ({
className='grow'
variant='secondary'
>
<a href={getPluginLinkInMarketplace(payload, { language: locale, theme })} target='_blank' className='flex items-center gap-0.5'>
<a href={getPluginLinkInMarketplace(payload, marketplaceLinkParams)} target='_blank' className='flex items-center gap-0.5'>
{t('plugin.detailPanel.operation.detail')}
<RiArrowRightUpLine className='h-4 w-4' />
</a>
@ -96,4 +99,7 @@ const ProviderCard: FC<Props> = ({
)
}
// Memoize the component to prevent unnecessary re-renders when props haven't changed
const ProviderCard = React.memo(ProviderCardComponent)
export default ProviderCard