Skip to content

Commit 211d18a

Browse files
committed
refactor: consolidate ModesView state management and add cleanup
- Consolidate 7 separate state variables into a single editingState object - Add useEffect cleanup to prevent memory leaks on component unmount - Maintain same functionality while simplifying state management - Addresses UI state management complexity and potential memory leak issues
1 parent 043e35f commit 211d18a

File tree

1 file changed

+135
-63
lines changed

1 file changed

+135
-63
lines changed

webview-ui/src/components/modes/ModesView.tsx

Lines changed: 135 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,24 @@ const ModesView = ({ onDone }: ModesViewProps) => {
115115
const [searchValue, setSearchValue] = useState("")
116116
const searchInputRef = useRef<HTMLInputElement>(null)
117117

118-
// Local state for all editable fields to allow visual emptying but prevent saving empty values
119-
const [localModeName, setLocalModeName] = useState<string>("")
120-
const [localModeDescription, setLocalModeDescription] = useState<string>("")
121-
const [localModeRoleDefinition, setLocalModeRoleDefinition] = useState<string>("")
122-
const [localModeWhenToUse, setLocalModeWhenToUse] = useState<string>("")
123-
const [localModeCustomInstructions, setLocalModeCustomInstructions] = useState<string>("")
124-
const [currentEditingModeSlug, setCurrentEditingModeSlug] = useState<string | null>(null)
125-
const [currentEditingField, setCurrentEditingField] = useState<string | null>(null)
118+
// Consolidated local state for all editable fields to allow visual emptying but prevent saving empty values
119+
const [editingState, setEditingState] = useState<{
120+
modeName: string
121+
modeDescription: string
122+
modeRoleDefinition: string
123+
modeWhenToUse: string
124+
modeCustomInstructions: string
125+
currentEditingModeSlug: string | null
126+
currentEditingField: string | null
127+
}>({
128+
modeName: "",
129+
modeDescription: "",
130+
modeRoleDefinition: "",
131+
modeWhenToUse: "",
132+
modeCustomInstructions: "",
133+
currentEditingModeSlug: null,
134+
currentEditingField: null,
135+
})
126136

127137
// Direct update functions
128138
const updateAgentPrompt = useCallback(
@@ -234,16 +244,33 @@ const ModesView = ({ onDone }: ModesViewProps) => {
234244

235245
// Reset all local state when mode changes
236246
useEffect(() => {
237-
if (currentEditingModeSlug && currentEditingModeSlug !== visualMode) {
238-
setCurrentEditingModeSlug(null)
239-
setCurrentEditingField(null)
240-
setLocalModeName("")
241-
setLocalModeDescription("")
242-
setLocalModeRoleDefinition("")
243-
setLocalModeWhenToUse("")
244-
setLocalModeCustomInstructions("")
247+
if (editingState.currentEditingModeSlug && editingState.currentEditingModeSlug !== visualMode) {
248+
setEditingState({
249+
modeName: "",
250+
modeDescription: "",
251+
modeRoleDefinition: "",
252+
modeWhenToUse: "",
253+
modeCustomInstructions: "",
254+
currentEditingModeSlug: null,
255+
currentEditingField: null,
256+
})
257+
}
258+
}, [visualMode, editingState.currentEditingModeSlug])
259+
260+
// Cleanup state on component unmount to prevent memory leaks
261+
useEffect(() => {
262+
return () => {
263+
setEditingState({
264+
modeName: "",
265+
modeDescription: "",
266+
modeRoleDefinition: "",
267+
modeWhenToUse: "",
268+
modeCustomInstructions: "",
269+
currentEditingModeSlug: null,
270+
currentEditingField: null,
271+
})
245272
}
246-
}, [visualMode, currentEditingModeSlug])
273+
}, [])
247274

248275
// Helper function to safely access mode properties
249276
const getModeProperty = <T extends keyof ModeConfig>(
@@ -753,33 +780,42 @@ const ModesView = ({ onDone }: ModesViewProps) => {
753780
<Input
754781
type="text"
755782
value={
756-
currentEditingModeSlug === visualMode
757-
? localModeName
783+
editingState.currentEditingModeSlug === visualMode
784+
? editingState.modeName
758785
: (getModeProperty(findModeBySlug(visualMode, customModes), "name") ??
759786
"")
760787
}
761788
onFocus={() => {
762789
const customMode = findModeBySlug(visualMode, customModes)
763790
if (customMode) {
764-
setCurrentEditingModeSlug(visualMode)
765-
setLocalModeName(customMode.name)
791+
setEditingState(prev => ({
792+
...prev,
793+
currentEditingModeSlug: visualMode,
794+
modeName: customMode.name
795+
}))
766796
}
767797
}}
768798
onChange={(e) => {
769-
setLocalModeName(e.target.value)
799+
setEditingState(prev => ({
800+
...prev,
801+
modeName: e.target.value
802+
}))
770803
}}
771804
onBlur={() => {
772805
const customMode = findModeBySlug(visualMode, customModes)
773-
if (customMode && localModeName.trim()) {
806+
if (customMode && editingState.modeName.trim()) {
774807
// Only update if the name is not empty
775808
updateCustomMode(visualMode, {
776809
...customMode,
777-
name: localModeName,
810+
name: editingState.modeName,
778811
source: customMode.source || "global",
779812
})
780813
}
781814
// Clear the editing state
782-
setCurrentEditingModeSlug(null)
815+
setEditingState(prev => ({
816+
...prev,
817+
currentEditingModeSlug: null
818+
}))
783819
}}
784820
className="w-full"
785821
/>
@@ -842,8 +878,8 @@ const ModesView = ({ onDone }: ModesViewProps) => {
842878
const prompt = customModePrompts?.[visualMode] as PromptComponent
843879

844880
// Use local state if currently editing this field
845-
if (currentEditingField === "roleDefinition" && currentEditingModeSlug === visualMode) {
846-
return localModeRoleDefinition
881+
if (editingState.currentEditingField === "roleDefinition" && editingState.currentEditingModeSlug === visualMode) {
882+
return editingState.modeRoleDefinition
847883
}
848884

849885
return (
@@ -859,37 +895,46 @@ const ModesView = ({ onDone }: ModesViewProps) => {
859895
prompt?.roleDefinition ??
860896
getRoleDefinition(visualMode)
861897

862-
setCurrentEditingModeSlug(visualMode)
863-
setCurrentEditingField("roleDefinition")
864-
setLocalModeRoleDefinition(currentValue)
898+
setEditingState(prev => ({
899+
...prev,
900+
currentEditingModeSlug: visualMode,
901+
currentEditingField: "roleDefinition",
902+
modeRoleDefinition: currentValue
903+
}))
865904
}}
866905
onChange={(e) => {
867906
const value = extractEventValue(e)
868-
setLocalModeRoleDefinition(value)
907+
setEditingState(prev => ({
908+
...prev,
909+
modeRoleDefinition: value
910+
}))
869911
}}
870912
onBlur={() => {
871913
const customMode = findModeBySlug(visualMode, customModes)
872914

873915
// Only save if the value is not empty
874-
if (localModeRoleDefinition.trim()) {
916+
if (editingState.modeRoleDefinition.trim()) {
875917
if (customMode) {
876918
// For custom modes, update the JSON file
877919
updateCustomMode(visualMode, {
878920
...customMode,
879-
roleDefinition: localModeRoleDefinition.trim(),
921+
roleDefinition: editingState.modeRoleDefinition.trim(),
880922
source: customMode.source || "global",
881923
})
882924
} else {
883925
// For built-in modes, update the prompts
884926
updateAgentPrompt(visualMode, {
885-
roleDefinition: localModeRoleDefinition.trim(),
927+
roleDefinition: editingState.modeRoleDefinition.trim(),
886928
})
887929
}
888930
}
889931

890932
// Clear the editing state
891-
setCurrentEditingField(null)
892-
setCurrentEditingModeSlug(null)
933+
setEditingState(prev => ({
934+
...prev,
935+
currentEditingField: null,
936+
currentEditingModeSlug: null
937+
}))
893938
}}
894939
className="w-full"
895940
rows={5}
@@ -927,8 +972,8 @@ const ModesView = ({ onDone }: ModesViewProps) => {
927972
const prompt = customModePrompts?.[visualMode] as PromptComponent
928973

929974
// Use local state if currently editing this field
930-
if (currentEditingField === "description" && currentEditingModeSlug === visualMode) {
931-
return localModeDescription
975+
if (editingState.currentEditingField === "description" && editingState.currentEditingModeSlug === visualMode) {
976+
return editingState.modeDescription
932977
}
933978

934979
return customMode?.description ?? prompt?.description ?? getDescription(visualMode)
@@ -940,19 +985,25 @@ const ModesView = ({ onDone }: ModesViewProps) => {
940985
prompt?.description ??
941986
getDescription(visualMode)
942987

943-
setCurrentEditingModeSlug(visualMode)
944-
setCurrentEditingField("description")
945-
setLocalModeDescription(currentValue || "")
988+
setEditingState(prev => ({
989+
...prev,
990+
currentEditingModeSlug: visualMode,
991+
currentEditingField: "description",
992+
modeDescription: currentValue || ""
993+
}))
946994
}}
947995
onChange={(e) => {
948996
const value = extractEventValue(e)
949-
setLocalModeDescription(value)
997+
setEditingState(prev => ({
998+
...prev,
999+
modeDescription: value
1000+
}))
9501001
}}
9511002
onBlur={() => {
9521003
const customMode = findModeBySlug(visualMode, customModes)
9531004

9541005
// For description, allow empty values (they become undefined)
955-
const trimmedValue = localModeDescription.trim()
1006+
const trimmedValue = editingState.modeDescription.trim()
9561007
const finalValue = trimmedValue || undefined
9571008

9581009
if (customMode) {
@@ -970,8 +1021,11 @@ const ModesView = ({ onDone }: ModesViewProps) => {
9701021
}
9711022

9721023
// Clear the editing state
973-
setCurrentEditingField(null)
974-
setCurrentEditingModeSlug(null)
1024+
setEditingState(prev => ({
1025+
...prev,
1026+
currentEditingField: null,
1027+
currentEditingModeSlug: null
1028+
}))
9751029
}}
9761030
className="w-full"
9771031
data-testid={`${getCurrentMode()?.slug || "code"}-description-textfield`}
@@ -1009,8 +1063,8 @@ const ModesView = ({ onDone }: ModesViewProps) => {
10091063
const prompt = customModePrompts?.[visualMode] as PromptComponent
10101064

10111065
// Use local state if currently editing this field
1012-
if (currentEditingField === "whenToUse" && currentEditingModeSlug === visualMode) {
1013-
return localModeWhenToUse
1066+
if (editingState.currentEditingField === "whenToUse" && editingState.currentEditingModeSlug === visualMode) {
1067+
return editingState.modeWhenToUse
10141068
}
10151069

10161070
return customMode?.whenToUse ?? prompt?.whenToUse ?? getWhenToUse(visualMode)
@@ -1022,19 +1076,25 @@ const ModesView = ({ onDone }: ModesViewProps) => {
10221076
prompt?.whenToUse ??
10231077
getWhenToUse(visualMode)
10241078

1025-
setCurrentEditingModeSlug(visualMode)
1026-
setCurrentEditingField("whenToUse")
1027-
setLocalModeWhenToUse(currentValue || "")
1079+
setEditingState(prev => ({
1080+
...prev,
1081+
currentEditingModeSlug: visualMode,
1082+
currentEditingField: "whenToUse",
1083+
modeWhenToUse: currentValue || ""
1084+
}))
10281085
}}
10291086
onChange={(e) => {
10301087
const value = extractEventValue(e)
1031-
setLocalModeWhenToUse(value)
1088+
setEditingState(prev => ({
1089+
...prev,
1090+
modeWhenToUse: value
1091+
}))
10321092
}}
10331093
onBlur={() => {
10341094
const customMode = findModeBySlug(visualMode, customModes)
10351095

10361096
// For whenToUse, allow empty values (they become undefined)
1037-
const trimmedValue = localModeWhenToUse.trim()
1097+
const trimmedValue = editingState.modeWhenToUse.trim()
10381098
const finalValue = trimmedValue || undefined
10391099

10401100
if (customMode) {
@@ -1052,8 +1112,11 @@ const ModesView = ({ onDone }: ModesViewProps) => {
10521112
}
10531113

10541114
// Clear the editing state
1055-
setCurrentEditingField(null)
1056-
setCurrentEditingModeSlug(null)
1115+
setEditingState(prev => ({
1116+
...prev,
1117+
currentEditingField: null,
1118+
currentEditingModeSlug: null
1119+
}))
10571120
}}
10581121
className="w-full"
10591122
rows={4}
@@ -1191,8 +1254,8 @@ const ModesView = ({ onDone }: ModesViewProps) => {
11911254
const prompt = customModePrompts?.[visualMode] as PromptComponent
11921255

11931256
// Use local state if currently editing this field
1194-
if (currentEditingField === "customInstructions" && currentEditingModeSlug === visualMode) {
1195-
return localModeCustomInstructions
1257+
if (editingState.currentEditingField === "customInstructions" && editingState.currentEditingModeSlug === visualMode) {
1258+
return editingState.modeCustomInstructions
11961259
}
11971260

11981261
return (
@@ -1208,19 +1271,25 @@ const ModesView = ({ onDone }: ModesViewProps) => {
12081271
prompt?.customInstructions ??
12091272
getCustomInstructions(mode, customModes)
12101273

1211-
setCurrentEditingModeSlug(visualMode)
1212-
setCurrentEditingField("customInstructions")
1213-
setLocalModeCustomInstructions(currentValue || "")
1274+
setEditingState(prev => ({
1275+
...prev,
1276+
currentEditingModeSlug: visualMode,
1277+
currentEditingField: "customInstructions",
1278+
modeCustomInstructions: currentValue || ""
1279+
}))
12141280
}}
12151281
onChange={(e) => {
12161282
const value = extractEventValue(e)
1217-
setLocalModeCustomInstructions(value)
1283+
setEditingState(prev => ({
1284+
...prev,
1285+
modeCustomInstructions: value
1286+
}))
12181287
}}
12191288
onBlur={() => {
12201289
const customMode = findModeBySlug(visualMode, customModes)
12211290

12221291
// For customInstructions, allow empty values (they become undefined)
1223-
const trimmedValue = localModeCustomInstructions.trim()
1292+
const trimmedValue = editingState.modeCustomInstructions.trim()
12241293
const finalValue = trimmedValue || undefined
12251294

12261295
if (customMode) {
@@ -1240,8 +1309,11 @@ const ModesView = ({ onDone }: ModesViewProps) => {
12401309
}
12411310

12421311
// Clear the editing state
1243-
setCurrentEditingField(null)
1244-
setCurrentEditingModeSlug(null)
1312+
setEditingState(prev => ({
1313+
...prev,
1314+
currentEditingField: null,
1315+
currentEditingModeSlug: null
1316+
}))
12451317
}}
12461318
rows={10}
12471319
className="w-full"

0 commit comments

Comments
 (0)