Fix broken React usage with dropdowns causing unnecessary re-renders in preferences (#95904)

Fixes
https://github.com/tgstation/tgstation/issues/95860#issuecomment-4320548139.
Thank you to @leaKsi for isolating it

React lints would've caught this--components not being components'd
properly and then an inline callback ended up causing a bunch of
re-renders.

I didn't test shit

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Mothblocks
2026-05-05 20:36:00 -07:00
committed by GitHub
parent 80cf6f86a1
commit 53fe04a572
2 changed files with 54 additions and 22 deletions
@@ -1,7 +1,9 @@
import { useCallback } from 'react';
import { Gender } from '../../gender';
import type { FeatureChoiced } from '../base';
import type { FeatureChoicedServerData, FeatureChoiced } from '../base';
import {
type DropdownInputProps,
type DropdownOptions,
FeatureDropdownInputCore,
generateOptions,
} from '../dropdowns';
@@ -12,17 +14,29 @@ export const body_type: FeatureChoiced = {
};
function FeatureBodyTypeDropdownInput(props: DropdownInputProps) {
return FeatureDropdownInputCore(props, (serverData, setDropdownOptions) => {
let options = generateOptions(serverData);
const currentGender = props.character_preferences.misc.gender;
const current_gender = props.character_preferences.misc.gender;
if (current_gender !== Gender.Male && current_gender !== Gender.Female) {
options = options.filter(
(option) =>
option.value === Gender.Male || option.value === Gender.Female,
);
}
const populateOptions = useCallback(
(
serverData: FeatureChoicedServerData,
setDropdownOptions: (newValue: DropdownOptions) => void
) => {
let options = generateOptions(serverData);
setDropdownOptions(options);
});
if (currentGender !== Gender.Male && currentGender !== Gender.Female) {
options = options.filter(
(option) =>
option.value === Gender.Male || option.value === Gender.Female,
);
}
setDropdownOptions(options);
},
[currentGender],
);
return <FeatureDropdownInputCore
dropdownProps={props}
populateOptions={populateOptions}
/>
}
@@ -1,6 +1,7 @@
import {
type ComponentProps,
type ReactNode,
useCallback,
useEffect,
useState,
} from 'react';
@@ -32,7 +33,7 @@ type IconnedDropdownInputProps = FeatureValueProps<
export type FeatureWithIcons<T> = Feature<string, T, FeatureChoicedServerData>;
type DropdownOptions = ComponentProps<typeof Dropdown>['options'];
export type DropdownOptions = ComponentProps<typeof Dropdown>['options'];
type DropdownEntry = {
displayText: ReactNode;
@@ -60,20 +61,20 @@ export function generateOptions(
return newOptions;
}
export function FeatureDropdownInput(props: DropdownInputProps) {
return FeatureDropdownInputCore(props, (serverData, setDropdownOptions) =>
setDropdownOptions(generateOptions(serverData)),
);
}
export function FeatureDropdownInputCore(
props: DropdownInputProps,
export type FeatureDropdownInputCoreProps = {
dropdownProps: DropdownInputProps,
populateOptions: (
serverData: FeatureChoicedServerData,
setDropdownOptions: (newValue: DropdownOptions) => void,
) => void,
}
export function FeatureDropdownInputCore(
props: FeatureDropdownInputCoreProps
) {
const { serverData, disabled, buttons, handleSetValue, value } = props;
const { dropdownProps, populateOptions } = props;
const { serverData, disabled, buttons, handleSetValue, value } =
dropdownProps;
const [dropdownOptions, setDropdownOptions] = useState<DropdownOptions>([]);
@@ -98,6 +99,23 @@ export function FeatureDropdownInputCore(
);
}
export function FeatureDropdownInput(props: DropdownInputProps) {
const populateOptions = useCallback(
(
serverData: FeatureChoicedServerData,
setDropdownOptions: (newValue: DropdownOptions) => void
) => {
setDropdownOptions(generateOptions(serverData));
},
[],
);
return <FeatureDropdownInputCore
dropdownProps={props}
populateOptions={populateOptions}
/>
}
export function FeatureIconnedDropdownInput(props: IconnedDropdownInputProps) {
const { serverData, handleSetValue, value } = props;