React数组元素删除失败求助:父子组件数据形态异常排查
Hey there! I totally get how frustrating this can be—when your array suddenly turns into some weird nested object right when you need to delete an element, it feels like you're chasing a ghost. Let's dig into why this happens with Formik and fix it for good.
This almost always boils down to how Formik wraps form values when you use tools like FieldArray or getFieldProps. Here's the key issue:
- When you pass a Formik-managed array directly from parent to child (without extracting the raw value), you're actually passing a Formik field object—not the plain array. This object has extra properties like
name,onChange, andvalue(where your actual array lives), so it looks like a nested structure instead of a simple array. - Sometimes child components accidentally wrap the array in another object when passing data back to the parent, compounding the issue.
1. Extract the Raw Array From Formik's Field Wrapper
If you're using Formik's FieldArray, make sure you pass the value property of the field object to your child component—not the entire field object itself. That value is your plain, original array.
2. Use Formik's Built-in Array Helpers for Deletions
Formik's FieldArray provides methods like remove() that handle updating the form state correctly. Instead of trying to manipulate the array directly, pass these helpers down to your child component to trigger deletions.
3. Avoid Accidentally Wrapping the Array in Extra Objects
Double-check your child component code: if you're sending data back to the parent, make sure you're passing the plain array, not wrapping it in an object (like { items: updatedArray }). Keep it simple—pass the array directly.
Here's a complete code snippet that keeps your array in its original form and lets you delete elements in real-time:
Parent Component (With Formik)
import { Formik, Form, FieldArray } from 'formik'; const ParentComponent = () => { // Initial form state with a plain array const initialValues = { items: [ { id: 1, name: "First Item" }, { id: 2, name: "Second Item" }, { id: 3, name: "Third Item" } ] }; const handleSubmit = (values) => { console.log("Submitted items:", values.items); // This will still be a plain array! }; return ( <Formik initialValues={initialValues} onSubmit={handleSubmit}> {() => ( <Form> {/* Use FieldArray to manage the list */} <FieldArray name="items"> {({ field, remove }) => ( // Pass ONLY the raw array (field.value) and the remove method <ChildComponent items={field.value} onDeleteItem={(index) => remove(index)} /> )} </FieldArray> <button type="submit">Submit Form</button> </Form> )} </Formik> ); };
Child Component (Renders & Deletes Items)
const ChildComponent = ({ items, onDeleteItem }) => { return ( <div className="item-list"> {items.map((item, index) => ( <div key={item.id} className="item"> <span>{item.name}</span> {/* Call the parent's delete handler with the index */} <button onClick={() => onDeleteItem(index)}>Delete</button> </div> ))} </div> ); };
If you're not using FieldArray and managing the array manually:
- In the parent, pass
form.values.yourArrayName(the raw array) to the child. - When deleting in the child, create a new array (don't mutate the original!), then call
form.setFieldValue('yourArrayName', newArray)in the parent to update Formik's state.
This should keep your array in its proper form and let you delete elements without any weird object structure surprises.
内容的提问来源于stack exchange,提问作者Stephen Smith

