From 5ff5af3ba28e4326969c109d2aca3c1b4441aee5 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Wed, 19 Jun 2024 00:06:06 +1000 Subject: [PATCH] perf(ui): fix lag w/ region rendering Needed to memoize these selectors --- .../ControlAdapter/CAActionsMenu.tsx | 33 ++++++++------- .../components/Layer/LayerActionsMenu.tsx | 33 ++++++++------- .../RegionalGuidance/RGActionsMenu.tsx | 40 +++++++++---------- 3 files changed, 56 insertions(+), 50 deletions(-) diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/CAActionsMenu.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/CAActionsMenu.tsx index 1219a1d226..2bf7537377 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/CAActionsMenu.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/CAActionsMenu.tsx @@ -1,5 +1,5 @@ import { Menu, MenuItem, MenuList } from '@invoke-ai/ui-library'; -import { createAppSelector } from 'app/store/createMemoizedSelector'; +import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { CanvasEntityMenuButton } from 'features/controlLayers/components/common/CanvasEntityMenuButton'; import { @@ -11,7 +11,7 @@ import { selectCanvasV2Slice, } from 'features/controlLayers/store/canvasV2Slice'; import { selectCAOrThrow } from 'features/controlLayers/store/controlAdaptersReducers'; -import { memo, useCallback } from 'react'; +import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { PiArrowDownBold, @@ -25,22 +25,25 @@ type Props = { id: string; }; -const selectValidActions = createAppSelector([selectCanvasV2Slice, (canvasV2, id: string) => id], (canvasV2, id) => { - const ca = selectCAOrThrow(canvasV2, id); - const caIndex = canvasV2.controlAdapters.indexOf(ca); - const caCount = canvasV2.controlAdapters.length; - return { - canMoveForward: caIndex < caCount - 1, - canMoveBackward: caIndex > 0, - canMoveToFront: caIndex < caCount - 1, - canMoveToBack: caIndex > 0, - }; -}); - export const CAActionsMenu = memo(({ id }: Props) => { const { t } = useTranslation(); const dispatch = useAppDispatch(); - const validActions = useAppSelector((s) => selectValidActions(s, id)); + const selectValidActions = useMemo( + () => + createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => { + const ca = selectCAOrThrow(canvasV2, id); + const caIndex = canvasV2.controlAdapters.indexOf(ca); + const caCount = canvasV2.controlAdapters.length; + return { + canMoveForward: caIndex < caCount - 1, + canMoveBackward: caIndex > 0, + canMoveToFront: caIndex < caCount - 1, + canMoveToBack: caIndex > 0, + }; + }), + [id] + ); + const validActions = useAppSelector(selectValidActions); const onDelete = useCallback(() => { dispatch(caDeleted({ id })); }, [dispatch, id]); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Layer/LayerActionsMenu.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Layer/LayerActionsMenu.tsx index 5498ba6ade..6c161531dd 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/Layer/LayerActionsMenu.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/Layer/LayerActionsMenu.tsx @@ -1,5 +1,5 @@ import { Menu, MenuItem, MenuList } from '@invoke-ai/ui-library'; -import { createMemoizedAppSelector } from 'app/store/createMemoizedSelector'; +import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { CanvasEntityMenuButton } from 'features/controlLayers/components/common/CanvasEntityMenuButton'; import { @@ -11,7 +11,7 @@ import { selectCanvasV2Slice, } from 'features/controlLayers/store/canvasV2Slice'; import { selectLayerOrThrow } from 'features/controlLayers/store/layersReducers'; -import { memo, useCallback } from 'react'; +import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { PiArrowDownBold, @@ -25,22 +25,25 @@ type Props = { id: string; }; -const selectValidActions = createMemoizedAppSelector([selectCanvasV2Slice, (canvasV2, id: string) => id], (canvasV2, id) => { - const layer = selectLayerOrThrow(canvasV2, id); - const layerIndex = canvasV2.layers.indexOf(layer); - const layerCount = canvasV2.layers.length; - return { - canMoveForward: layerIndex < layerCount - 1, - canMoveBackward: layerIndex > 0, - canMoveToFront: layerIndex < layerCount - 1, - canMoveToBack: layerIndex > 0, - }; -}); - export const LayerActionsMenu = memo(({ id }: Props) => { const { t } = useTranslation(); const dispatch = useAppDispatch(); - const validActions = useAppSelector((s) => selectValidActions(s, id)); + const selectValidActions = useMemo( + () => + createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => { + const layer = selectLayerOrThrow(canvasV2, id); + const layerIndex = canvasV2.layers.indexOf(layer); + const layerCount = canvasV2.layers.length; + return { + canMoveForward: layerIndex < layerCount - 1, + canMoveBackward: layerIndex > 0, + canMoveToFront: layerIndex < layerCount - 1, + canMoveToBack: layerIndex > 0, + }; + }), + [id] + ); + const validActions = useAppSelector(selectValidActions); const onDelete = useCallback(() => { dispatch(layerDeleted({ id })); }, [dispatch, id]); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RGActionsMenu.tsx b/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RGActionsMenu.tsx index 94107c9c0f..1df3467959 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RGActionsMenu.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RGActionsMenu.tsx @@ -1,5 +1,5 @@ import { Menu, MenuDivider, MenuItem, MenuList } from '@invoke-ai/ui-library'; -import { createMemoizedAppSelector } from 'app/store/createMemoizedSelector'; +import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { CanvasEntityMenuButton } from 'features/controlLayers/components/common/CanvasEntityMenuButton'; import { useAddIPAdapterToRGLayer } from 'features/controlLayers/hooks/addLayerHooks'; @@ -15,7 +15,7 @@ import { selectCanvasV2Slice, } from 'features/controlLayers/store/canvasV2Slice'; import { selectRGOrThrow } from 'features/controlLayers/store/regionsReducers'; -import { memo, useCallback } from 'react'; +import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { PiArrowCounterClockwiseBold, @@ -31,28 +31,28 @@ type Props = { id: string; }; -const selectActionsValidity = createMemoizedAppSelector( - [selectCanvasV2Slice, (canvasV2, id: string) => id], - (canvasV2, id) => { - const rg = selectRGOrThrow(canvasV2, id); - const rgIndex = canvasV2.regions.indexOf(rg); - const rgCount = canvasV2.regions.length; - return { - isMoveForwardOneDisabled: rgIndex < rgCount - 1, - isMoveBackardOneDisabled: rgIndex > 0, - isMoveToFrontDisabled: rgIndex < rgCount - 1, - isMoveToBackDisabled: rgIndex > 0, - isAddPositivePromptDisabled: rg.positivePrompt === null, - isAddNegativePromptDisabled: rg.negativePrompt === null, - }; - } -); - export const RGActionsMenu = memo(({ id }: Props) => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const [onAddIPAdapter, isAddIPAdapterDisabled] = useAddIPAdapterToRGLayer(id); - const actions = useAppSelector((s) => selectActionsValidity(s, id)); + const selectActionsValidity = useMemo( + () => + createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => { + const rg = selectRGOrThrow(canvasV2, id); + const rgIndex = canvasV2.regions.indexOf(rg); + const rgCount = canvasV2.regions.length; + return { + isMoveForwardOneDisabled: rgIndex < rgCount - 1, + isMoveBackardOneDisabled: rgIndex > 0, + isMoveToFrontDisabled: rgIndex < rgCount - 1, + isMoveToBackDisabled: rgIndex > 0, + isAddPositivePromptDisabled: rg.positivePrompt === null, + isAddNegativePromptDisabled: rg.negativePrompt === null, + }; + }), + [id] + ); + const actions = useAppSelector(selectActionsValidity); const onDelete = useCallback(() => { dispatch(rgDeleted({ id })); }, [dispatch, id]);