perf(ui): optimize all selectors 1

I learned that the inline selector syntax recreates the selector function on every render:

```ts
const val = useAppSelector((s) => s.slice.val)
```

Not good! Better is to create a selector outside the function and use it. Doing that for all selectors now, most of the way through now. Feels snappier.
This commit is contained in:
psychedelicious
2024-08-27 13:19:14 +10:00
parent 04f78a99ad
commit bac0ce1e69
92 changed files with 561 additions and 294 deletions

View File

@@ -1,18 +1,20 @@
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { maxPromptsChanged } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
import { maxPromptsChanged, selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
import { selectConfigSlice } from 'features/system/store/configSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const selectMaxPrompts = createSelector(selectDynamicPromptsSlice, (dynamicPrompts) => dynamicPrompts.maxPrompts);
const selectMaxPromptsConfig = createSelector(selectConfigSlice, (config) => config.sd.dynamicPrompts.maxPrompts);
const selectIsDisabled = createSelector(selectDynamicPromptsSlice, (dynamicPrompts) => !dynamicPrompts.combinatorial);
const ParamDynamicPromptsMaxPrompts = () => {
const maxPrompts = useAppSelector((s) => s.dynamicPrompts.maxPrompts);
const sliderMin = useAppSelector((s) => s.config.sd.dynamicPrompts.maxPrompts.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.dynamicPrompts.maxPrompts.sliderMax);
const numberInputMin = useAppSelector((s) => s.config.sd.dynamicPrompts.maxPrompts.numberInputMin);
const numberInputMax = useAppSelector((s) => s.config.sd.dynamicPrompts.maxPrompts.numberInputMax);
const initial = useAppSelector((s) => s.config.sd.dynamicPrompts.maxPrompts.initial);
const isDisabled = useAppSelector((s) => !s.dynamicPrompts.combinatorial);
const maxPrompts = useAppSelector(selectMaxPrompts);
const config = useAppSelector(selectMaxPromptsConfig);
const isDisabled = useAppSelector(selectIsDisabled);
const dispatch = useAppDispatch();
const { t } = useTranslation();
@@ -29,18 +31,18 @@ const ParamDynamicPromptsMaxPrompts = () => {
<FormLabel>{t('dynamicPrompts.maxPrompts')}</FormLabel>
</InformationalPopover>
<CompositeSlider
min={sliderMin}
max={sliderMax}
min={config.sliderMin}
max={config.sliderMax}
value={maxPrompts}
defaultValue={initial}
defaultValue={config.initial}
onChange={handleChange}
marks
/>
<CompositeNumberInput
min={numberInputMin}
max={numberInputMax}
min={config.numberInputMin}
max={config.numberInputMax}
value={maxPrompts}
defaultValue={initial}
defaultValue={config.initial}
onChange={handleChange}
/>
</FormControl>

View File

@@ -1,6 +1,6 @@
import type { ChakraProps } from '@invoke-ai/ui-library';
import { Flex, FormControl, FormLabel, ListItem, OrderedList, Spinner, Text } from '@invoke-ai/ui-library';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks';
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
@@ -10,17 +10,20 @@ import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiWarningCircleBold } from 'react-icons/pi';
const selectPrompts = createMemoizedSelector(selectDynamicPromptsSlice, (dynamicPrompts) => dynamicPrompts.prompts);
const listItemStyles: ChakraProps['sx'] = {
'&::marker': { color: 'base.500' },
};
const selectPrompts = createSelector(selectDynamicPromptsSlice, (dynamicPrompts) => dynamicPrompts.prompts);
const selectParsingError = createSelector(selectDynamicPromptsSlice, (dynamicPrompts) => dynamicPrompts.parsingError);
const selectIsError = createSelector(selectDynamicPromptsSlice, (dynamicPrompts) => dynamicPrompts.isError);
const selectIsLoading = createSelector(selectDynamicPromptsSlice, (dynamicPrompts) => dynamicPrompts.isLoading);
const ParamDynamicPromptsPreview = () => {
const { t } = useTranslation();
const parsingError = useAppSelector((s) => s.dynamicPrompts.parsingError);
const isError = useAppSelector((s) => s.dynamicPrompts.isError);
const isLoading = useAppSelector((s) => s.dynamicPrompts.isLoading);
const parsingError = useAppSelector(selectParsingError);
const isError = useAppSelector(selectIsError);
const isLoading = useAppSelector(selectIsLoading);
const prompts = useAppSelector(selectPrompts);
const label = useMemo(() => {

View File

@@ -1,15 +1,22 @@
import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library';
import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { isSeedBehaviour, seedBehaviourChanged } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
import {
isSeedBehaviour,
seedBehaviourChanged,
selectDynamicPromptsSlice,
} from 'features/dynamicPrompts/store/dynamicPromptsSlice';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
const selectSeedBehaviour = createSelector(selectDynamicPromptsSlice, (dynamicPrompts) => dynamicPrompts.seedBehaviour);
const ParamDynamicPromptsSeedBehaviour = () => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const seedBehaviour = useAppSelector((s) => s.dynamicPrompts.seedBehaviour);
const seedBehaviour = useAppSelector(selectSeedBehaviour);
const options = useMemo<ComboboxOption[]>(() => {
return [

View File

@@ -1,7 +1,9 @@
import type { SystemStyleObject } from '@invoke-ai/ui-library';
import { IconButton, spinAnimation, Tooltip } from '@invoke-ai/ui-library';
import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks';
import { useDynamicPromptsModal } from 'features/dynamicPrompts/hooks/useDynamicPromptsModal';
import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { BsBracesAsterisk } from 'react-icons/bs';
@@ -10,10 +12,15 @@ const loadingStyles: SystemStyleObject = {
svg: { animation: spinAnimation },
};
const selectIsError = createSelector(selectDynamicPromptsSlice, (dynamicPrompts) =>
Boolean(dynamicPrompts.isError || dynamicPrompts.parsingError)
);
const selectIsLoading = createSelector(selectDynamicPromptsSlice, (dynamicPrompts) => dynamicPrompts.isLoading);
export const ShowDynamicPromptsPreviewButton = memo(() => {
const { t } = useTranslation();
const isLoading = useAppSelector((s) => s.dynamicPrompts.isLoading);
const isError = useAppSelector((s) => Boolean(s.dynamicPrompts.isError || s.dynamicPrompts.parsingError));
const isLoading = useAppSelector(selectIsLoading);
const isError = useAppSelector(selectIsError);
const { isOpen, onOpen } = useDynamicPromptsModal();
return (