From 987e401709cd983208f1ed4358f93ae0f2d82db3 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 4 Jul 2025 01:39:59 +1000 Subject: [PATCH] perf(ui): lora components --- .../src/features/lora/components/LoRACard.tsx | 36 +++++++++++++------ .../src/features/lora/components/LoRAList.tsx | 10 +++--- .../features/lora/components/LoRASelect.tsx | 12 ++++--- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/invokeai/frontend/web/src/features/lora/components/LoRACard.tsx b/invokeai/frontend/web/src/features/lora/components/LoRACard.tsx index 28c0ea8198..dd26a8e73b 100644 --- a/invokeai/frontend/web/src/features/lora/components/LoRACard.tsx +++ b/invokeai/frontend/web/src/features/lora/components/LoRACard.tsx @@ -9,22 +9,38 @@ import { Switch, Text, } from '@invoke-ai/ui-library'; -import { useAppDispatch } from 'app/store/storeHooks'; +import { createSelector } from '@reduxjs/toolkit'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover'; -import { loraDeleted, loraIsEnabledChanged, loraWeightChanged } from 'features/controlLayers/store/lorasSlice'; +import { + loraDeleted, + loraIsEnabledChanged, + loraWeightChanged, + selectLoRAsSlice, +} from 'features/controlLayers/store/lorasSlice'; import type { LoRA } from 'features/controlLayers/store/types'; -import { memo, useCallback } from 'react'; +import { memo, useCallback, useMemo } from 'react'; import { PiTrashSimpleBold } from 'react-icons/pi'; import { useGetModelConfigQuery } from 'services/api/endpoints/models'; -type LoRACardProps = { - lora: LoRA; -}; - const marks = [-1, 0, 1, 2]; -export const LoRACard = memo((props: LoRACardProps) => { - const { lora } = props; +export const LoRACard = memo((props: { id: string }) => { + const selectLoRA = useMemo( + () => createSelector(selectLoRAsSlice, ({ loras }) => loras.find(({ id }) => id === props.id)), + [props.id] + ); + const lora = useAppSelector(selectLoRA); + + if (!lora) { + return null; + } + return ; +}); + +LoRACard.displayName = 'LoRACard'; + +export const LoRAContent = memo(({ lora }: { lora: LoRA }) => { const dispatch = useAppDispatch(); const { data: loraConfig } = useGetModelConfigQuery(lora.model.key); @@ -91,4 +107,4 @@ export const LoRACard = memo((props: LoRACardProps) => { ); }); -LoRACard.displayName = 'LoRACard'; +LoRAContent.displayName = 'LoRAContent'; diff --git a/invokeai/frontend/web/src/features/lora/components/LoRAList.tsx b/invokeai/frontend/web/src/features/lora/components/LoRAList.tsx index 6d05f1ea6f..5e43ab9223 100644 --- a/invokeai/frontend/web/src/features/lora/components/LoRAList.tsx +++ b/invokeai/frontend/web/src/features/lora/components/LoRAList.tsx @@ -5,19 +5,19 @@ import { selectLoRAsSlice } from 'features/controlLayers/store/lorasSlice'; import { LoRACard } from 'features/lora/components/LoRACard'; import { memo } from 'react'; -const selectLoRAsArray = createMemoizedSelector(selectLoRAsSlice, (loras) => loras.loras); +const selectLoRAIds = createMemoizedSelector(selectLoRAsSlice, (loras) => loras.loras.map(({ id }) => id)); export const LoRAList = memo(() => { - const lorasArray = useAppSelector(selectLoRAsArray); + const ids = useAppSelector(selectLoRAIds); - if (!lorasArray.length) { + if (!ids.length) { return null; } return ( - {lorasArray.map((lora) => ( - + {ids.map((id) => ( + ))} ); diff --git a/invokeai/frontend/web/src/features/lora/components/LoRASelect.tsx b/invokeai/frontend/web/src/features/lora/components/LoRASelect.tsx index c9a5f2f846..01c9cffc36 100644 --- a/invokeai/frontend/web/src/features/lora/components/LoRASelect.tsx +++ b/invokeai/frontend/web/src/features/lora/components/LoRASelect.tsx @@ -1,6 +1,6 @@ import { FormControl, FormLabel } from '@invoke-ai/ui-library'; -import { createSelector } from '@reduxjs/toolkit'; import { EMPTY_ARRAY } from 'app/store/constants'; +import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover'; import type { GroupStatusMap } from 'common/components/Picker/Picker'; @@ -13,13 +13,15 @@ import { useTranslation } from 'react-i18next'; import { useLoRAModels } from 'services/api/hooks/modelsByType'; import type { LoRAModelConfig } from 'services/api/types'; -const selectLoRAs = createSelector(selectLoRAsSlice, (loras) => loras.loras); +const selectLoRAModelKeys = createMemoizedSelector(selectLoRAsSlice, ({ loras }) => + loras.map(({ model }) => model.key) +); const LoRASelect = () => { const dispatch = useAppDispatch(); const [modelConfigs, { isLoading }] = useLoRAModels(); const { t } = useTranslation(); - const addedLoRAs = useAppSelector(selectLoRAs); + const addedLoRAModelKeys = useAppSelector(selectLoRAModelKeys); const currentBaseModel = useAppSelector(selectBase); @@ -33,10 +35,10 @@ const LoRASelect = () => { const getIsDisabled = useCallback( (model: LoRAModelConfig): boolean => { - const isAdded = Boolean(addedLoRAs.find((lora) => lora.model.key === model.key)); + const isAdded = addedLoRAModelKeys.includes(model.key); return isAdded; }, - [addedLoRAs] + [addedLoRAModelKeys] ); const onChange = useCallback(