perf(ModelList): use Map for O(1) model status lookup (#12161)

- Replace Array.find() with Map.get() for modelStatus lookup
- Add useMemo to create modelStatusMap from modelStatuses array
- Stabilize onEditModel callback with useCallback to prevent memo invalidation

Fixes #12035

Signed-off-by: SherlockShemol <shemol@163.com>
This commit is contained in:
Shemol 2025-12-27 13:57:33 +08:00 committed by GitHub
parent 9586f38157
commit 723fa11647
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 14 additions and 5 deletions

View File

@ -49,6 +49,9 @@ const ModelList: React.FC<ModelListProps> = ({ providerId }) => {
const { t } = useTranslation() const { t } = useTranslation()
const { provider, models, removeModel } = useProvider(providerId) const { provider, models, removeModel } = useProvider(providerId)
// 稳定的编辑模型回调,避免内联函数导致子组件 memo 失效
const handleEditModel = useCallback((model: Model) => EditModelPopup.show({ provider, model }), [provider])
const providerConfig = PROVIDER_URLS[provider.id] const providerConfig = PROVIDER_URLS[provider.id]
const docsWebsite = providerConfig?.websites?.docs const docsWebsite = providerConfig?.websites?.docs
const modelsWebsite = providerConfig?.websites?.models const modelsWebsite = providerConfig?.websites?.models
@ -63,6 +66,11 @@ const ModelList: React.FC<ModelListProps> = ({ providerId }) => {
const { isChecking: isHealthChecking, modelStatuses, runHealthCheck } = useHealthCheck(provider, models) const { isChecking: isHealthChecking, modelStatuses, runHealthCheck } = useHealthCheck(provider, models)
// 将 modelStatuses 数组转换为 Map实现 O(1) 查找
const modelStatusMap = useMemo(() => {
return new Map(modelStatuses.map((status) => [status.model.id, status]))
}, [modelStatuses])
const setSearchText = useCallback((text: string) => { const setSearchText = useCallback((text: string) => {
startTransition(() => { startTransition(() => {
_setSearchText(text) _setSearchText(text)
@ -138,9 +146,9 @@ const ModelList: React.FC<ModelListProps> = ({ providerId }) => {
key={group} key={group}
groupName={group} groupName={group}
models={displayedModelGroups[group]} models={displayedModelGroups[group]}
modelStatuses={modelStatuses} modelStatusMap={modelStatusMap}
defaultOpen={i <= 5} defaultOpen={i <= 5}
onEditModel={(model) => EditModelPopup.show({ provider, model })} onEditModel={handleEditModel}
onRemoveModel={removeModel} onRemoveModel={removeModel}
onRemoveGroup={() => displayedModelGroups[group].forEach((model) => removeModel(model))} onRemoveGroup={() => displayedModelGroups[group].forEach((model) => removeModel(model))}
/> />

View File

@ -15,7 +15,8 @@ const MAX_SCROLLER_HEIGHT = 390
interface ModelListGroupProps { interface ModelListGroupProps {
groupName: string groupName: string
models: Model[] models: Model[]
modelStatuses: ModelWithStatus[] /** 使用 Map 实现 O(1) 查找,替代原来的数组线性搜索 */
modelStatusMap: Map<string, ModelWithStatus>
defaultOpen: boolean defaultOpen: boolean
disabled?: boolean disabled?: boolean
onEditModel: (model: Model) => void onEditModel: (model: Model) => void
@ -26,7 +27,7 @@ interface ModelListGroupProps {
const ModelListGroup: React.FC<ModelListGroupProps> = ({ const ModelListGroup: React.FC<ModelListGroupProps> = ({
groupName, groupName,
models, models,
modelStatuses, modelStatusMap,
defaultOpen, defaultOpen,
disabled, disabled,
onEditModel, onEditModel,
@ -89,7 +90,7 @@ const ModelListGroup: React.FC<ModelListGroupProps> = ({
{(model) => ( {(model) => (
<ModelListItem <ModelListItem
model={model} model={model}
modelStatus={modelStatuses.find((status) => status.model.id === model.id)} modelStatus={modelStatusMap.get(model.id)}
onEdit={onEditModel} onEdit={onEditModel}
onRemove={onRemoveModel} onRemove={onRemoveModel}
disabled={disabled} disabled={disabled}