fix (AgentFlow) - generate ArrayInput item keys synchronously to prevent missing React keys (#6001)

* Fix to remove useEffect

* Fix gemini comment
This commit is contained in:
j-sanaa
2026-03-17 13:12:19 -07:00
committed by GitHub
parent b2afa109d9
commit e39b9ff037
+28 -11
View File
@@ -1,4 +1,4 @@
import { type ComponentType, useCallback, useEffect, useMemo, useRef } from 'react'
import { type ComponentType, useCallback, useMemo, useRef, useState } from 'react'
import { Box, Button, Chip, IconButton } from '@mui/material'
import { useTheme } from '@mui/material/styles'
@@ -42,16 +42,33 @@ export function ArrayInput({
[data.inputValues, inputParam.name]
)
// Stable keys for array items — avoids using index as React key
// Stable keys for array items — avoids using index as React key.
// Keys are held in state so they persist across renders. A local variable
// (`effectiveKeys`) is used for the current render pass so that newly
// generated keys are available immediately (setState alone would only
// take effect on the *next* render, leaving keys undefined in this one).
const idCounterRef = useRef(0)
const itemKeysRef = useRef<string[]>([])
// Grow keys array when new items appear (e.g. on mount or external data changes)
useEffect(() => {
while (itemKeysRef.current.length < arrayItems.length) {
itemKeysRef.current.push(`item-${idCounterRef.current++}`)
const [itemKeys, setItemKeys] = useState<string[]>(() => {
const initial: string[] = []
while (initial.length < arrayItems.length) {
initial.push(`item-${idCounterRef.current++}`)
}
}, [arrayItems.length])
return initial
})
let effectiveKeys = itemKeys
if (effectiveKeys.length < arrayItems.length) {
const nextKeys = [...effectiveKeys]
while (nextKeys.length < arrayItems.length) {
nextKeys.push(`item-${idCounterRef.current++}`)
}
setItemKeys(nextKeys)
effectiveKeys = nextKeys
} else if (effectiveKeys.length > arrayItems.length) {
const trimmed = effectiveKeys.slice(0, arrayItems.length)
setItemKeys(trimmed)
effectiveKeys = trimmed
}
// Use pre-computed itemParameters
// Falls back to raw field definitions for nested arrays without show/hide conditions.
@@ -113,7 +130,7 @@ export function ArrayInput({
const handleDeleteItem = useCallback(
(indexToDelete: number) => {
const updatedArrayItems = arrayItems.filter((_, i) => i !== indexToDelete)
itemKeysRef.current.splice(indexToDelete, 1)
setItemKeys((prev) => prev.filter((_, i) => i !== indexToDelete))
// Notify parent of change (parent will update props, causing re-render)
onDataChange?.({ inputParam, newValue: updatedArrayItems })
@@ -145,7 +162,7 @@ export function ArrayInput({
return (
<Box
key={itemKeysRef.current[index]}
key={effectiveKeys[index]}
sx={{
p: 2,
mt: 2,