Formik FieldArray索引异常:移除联系人时误删目标项
Formik FieldArray 移除联系人异常问题修复
你的移除功能出错,核心是误用了原始联系人列表的索引来操作Formik的FieldArray数组,加上几处逻辑错误,导致索引对应混乱,出现删错项、无法删除的问题。具体问题点:
- 可用联系人列表里的
handleRemoveContact用了contactList的原始索引调用arrayHelpers.remove(index),但这个索引和values.contacts数组的索引完全不对应,必然删错。 - 选中联系人列表里用
contact.index(原始列表索引)作为key,移除时直接用该索引操作FieldArray,而values.contacts的元素顺序和原始列表无关,索引完全不匹配。 - 添加联系人时用
arrayHelpers.insert(index, ...)插入到原始列表对应的位置,打乱了contacts数组的索引逻辑,进一步加剧移除混乱。 - 地址已添加的判断逻辑错误:用联系人名字(
owner)匹配地址数组,导致判断失效。
修复步骤
1. 修正表单值结构,移除冗余索引字段
直接存储联系人的唯一标识(address)和名称(owner),去掉无用的index:
interface IFormValues { subject: string; body: string; contacts: { address: string; owner: string }[]; }
2. 修正添加联系人逻辑
改用push追加元素,同时修正已添加判断的匹配字段:
const handleAddContact = () => { arrayHelpers.push({ address: contact.address, owner: contact.owner }); }; // 用contact.address匹配,判断是否已添加 const addressAlreadyAdded = values.contacts .map(item => item.address) .includes(contact.address);
3. 移除可用联系人里的错误移除按钮
可用联系人列表里的-按钮逻辑完全错误,直接删除即可。
4. 修正选中联系人的移除逻辑
用当前元素在values.contacts数组里的循环索引idx执行移除,同时用唯一的address作为key避免渲染异常:
values.contacts.map((contactItem, idx) => ( <div key={contactItem.address} className={'text-gray-400'}> {contactItem.owner} <button type='button' onClick={() => { arrayHelpers.remove(idx); }}> - </button> </div> ))
完整修复后代码
import { Formik, Form, Field, FieldArray, ErrorMessage } from 'formik'; import SubmitButton from './SubmitButton'; import * as Yup from 'yup'; import axios from 'axios'; import { showErrorTransactionToast } from '../../utils/toast'; import { Contact } from '@iexec/web3mail'; interface IFormValues { subject: string; body: string; contacts: { address: string; owner: string }[]; } const validationSchema = Yup.object({ subject: Yup.string().required('Please provide a subject'), body: Yup.string().required('Please provide a body'), contacts: Yup.array().min(1).required('Please provide at least one contact'), }); export const ContactListForm = ({ contactList }: { contactList: Contact[] }) => { const initialValues: IFormValues = { subject: '', body: '', contacts: [], }; const onSubmit = async ( values: IFormValues, { setSubmitting }: { setSubmitting: (isSubmitting: boolean) => void }, ) => { console.log(values); try { setSubmitting(true); await axios.post('/api/iexec/send-web3mail-to-addresses', { subject: values.subject, body: values.body, contacts: values.contacts, }); setSubmitting(false); } catch (error) { showErrorTransactionToast(error); } }; return ( <Formik initialValues={initialValues} enableReinitialize={true} onSubmit={onSubmit} validationSchema={validationSchema}> {({ isSubmitting, values }) => ( <Form> <div className='grid grid-cols-1 gap-6 border border-gray-700 rounded-xl p-6 bg-endnight'> <label className='block'> <span className='text-gray-100'>Subject</span> <Field type='text' id='subject' name='subject' className='mt-1 mb-1 block w-full rounded-xl border border-gray-700 bg-midnight shadow-sm focus:ring-opacity-50' placeholder='Type your subject here...' /> <span className='text-red-500'> <ErrorMessage name='subject' /> </span> </label> <label className='block'> <span className='text-gray-100'>Body</span> <Field as='textarea' rows='4' id='body' name='body' className='mt-1 mb-1 block w-full rounded-xl border border-gray-700 bg-midnight shadow-sm focus:ring-opacity-50' placeholder='Type the email body here...' /> <span className='text-red-500'> <ErrorMessage name='body' /> </span> </label> <FieldArray name='contacts' render={arrayHelpers => ( <div className={'flex flex-row space-x-10'}> <label className='block flex-auto'> <span className='text-gray-100'>Available Contacts</span> <div className={'overflow-y-auto overflow-x-visible h-24'}> {contactList && contactList.length > 0 ? ( contactList.map((contact) => { const handleAddContact = () => { arrayHelpers.push({ address: contact.address, owner: contact.owner, }); }; const addressAlreadyAdded = values.contacts .map(item => item.address) .includes(contact.address); return ( <div key={contact.address} className={`text-gray-400 ${addressAlreadyAdded ? 'hidden' : ''}`}> {contact.owner} <button type='button' onClick={handleAddContact} > + </button> </div> ); }) ) : ( <p>No Contacts</p> )} </div> <span className='text-red-500'> <ErrorMessage name='contacts' /> </span> </label> <label className='block flex-auto '> <span className='text-gray-100'>Selected Contacts</span> <div className={'overflow-y-auto overflow-x-visible w-auto h-24'}> {values.contacts.length > 0 ? ( values.contacts.map((contactItem, idx) => ( <div key={contactItem.address} className={'text-gray-400'}> {contactItem.owner} <button type='button' onClick={() => { arrayHelpers.remove(idx); }}> - </button> </div> )) ) : ( <p>No Contacts</p> )} </div> </label> </div> )} /> <SubmitButton isSubmitting={isSubmitting} label='Send' /> </div> </Form> )} </Formik> ); };
额外说明
- 所有列表项使用唯一的
address作为key,避免索引变化导致的React渲染异常 - 移除操作直接使用元素在
values.contacts中的真实索引,确保删除目标正确 - 修正了已添加判断的逻辑错误,现在能正确隐藏已选中的联系人
内容的提问来源于stack exchange,提问作者quent043
相关产品推荐
相关产品推荐

