Files
gruperly/.agents/skills/react-hook-form/references/array-separate-crud-operations.md
2026-09-04 16:49:24 -03:00

2.0 KiB

title, impact, impactDescription, tags
title impact impactDescription tags
Separate Sequential Field Array Operations MEDIUM-HIGH prevents state corruption from batched mutations array, useFieldArray, append, remove, sequential

Separate Sequential Field Array Operations

Chaining append() and remove() in the same handler can cause state corruption. Defer removals to a useEffect or separate user action to allow React to process renders between operations.

Incorrect (stacked operations cause state issues):

function ReplaceItemForm() {
  const { control } = useForm()
  const { fields, append, remove } = useFieldArray({ control, name: 'items' })

  const replaceItem = (indexToReplace: number, newItem: Item) => {
    remove(indexToReplace)  // Remove old item
    append(newItem)  // Immediately add new - state may be stale
  }

  return (
    <div>
      {fields.map((field, index) => (
        <ItemRow
          key={field.id}
          index={index}
          onReplace={(newItem) => replaceItem(index, newItem)}
        />
      ))}
    </div>
  )
}

Correct (use update for replacements, or defer operations):

function ReplaceItemForm() {
  const { control } = useForm()
  const { fields, update } = useFieldArray({ control, name: 'items' })

  const replaceItem = (indexToReplace: number, newItem: Item) => {
    update(indexToReplace, newItem)  // Single atomic operation
  }

  return (
    <div>
      {fields.map((field, index) => (
        <ItemRow
          key={field.id}
          index={index}
          onReplace={(newItem) => replaceItem(index, newItem)}
        />
      ))}
    </div>
  )
}

Alternative (defer removal with useEffect):

const [pendingRemoval, setPendingRemoval] = useState<number | null>(null)

useEffect(() => {
  if (pendingRemoval !== null) {
    remove(pendingRemoval)
    setPendingRemoval(null)
  }
}, [pendingRemoval, remove])

Reference: useFieldArray