ReactJS中使用useState时动态表单列表的状态共享问题
解决Ant Design Form.List动态折叠面板独立状态问题
出现所有面板共享isRent状态的核心原因是你用单个useState维护全局状态,而非将状态与每个列表项绑定。以下是两种实用解决方案:
方案一:将状态绑定到Form.List的表单字段(推荐)
把isRent作为每个表单项的内置字段,利用Ant Design Form的状态管理自动实现独立控制,无需额外维护状态:
import { Form, Select, InputNumber, Collapse, Button } from 'antd'; const { Panel } = Collapse; const DynamicForm = () => { return ( <Form> <Form.List name="properties"> {(fields, { add, remove }) => ( <> {fields.map((field, index) => ( <Collapse key={field.key} defaultActiveKey={[field.key]}> <Panel header={`房产信息 ${index + 1}`}> {/* 绑定当前项的isRent字段 */} <Form.Item {...field} name={[field.name, 'isRent']} label="房产类型" rules={[{ required: true, message: '请选择房产类型' }]} > <Select> <Select.Option value={false}>自有</Select.Option> <Select.Option value={true}>租赁</Select.Option> </Select> </Form.Item> {/* 根据当前项的isRent状态动态渲染租金输入框 */} <Form.Item noStyle shouldUpdate={(prevValues, currentValues) => prevValues.properties[index]?.isRent !== currentValues.properties[index]?.isRent } > {({ getFieldValue }) => { const isRent = getFieldValue(['properties', index, 'isRent']); return isRent ? ( <Form.Item name={[field.name, 'rent']} label="月租金" rules={[{ required: true, message: '请输入月租金' }]} > <InputNumber min={0} style={{ width: '100%' }} /> </Form.Item> ) : null; }} </Form.Item> <Button type="dashed" onClick={() => remove(field.name)}>删除</Button> </Panel> </Collapse> ))} <Button type="primary" onClick={() => add({ isRent: false })} style={{ marginTop: 16 }}> 添加房产 </Button> </> )} </Form.List> </Form> ); }; export default DynamicForm;
优势
- 状态与表单数据深度绑定,新增/删除面板时自动同步状态
- 无需手动维护额外的状态数组,避免同步问题
- 利用Form的
shouldUpdate精准监听当前项的状态变化,性能更优
方案二:用数组维护独立状态
如果不想将状态存入表单字段,可以用useState创建一个与列表项一一对应的状态数组:
import { useState } from 'react'; import { Form, Select, InputNumber, Collapse, Button } from 'antd'; const { Panel } = Collapse; const DynamicForm = () => { // 用数组存储每个面板的isRent状态,默认都是false const [rentStates, setRentStates] = useState([]); return ( <Form> <Form.List name="properties"> {(fields, { add, remove }) => ( <> {/* 同步状态数组与列表项长度 */} {rentStates.length !== fields.length && setRentStates(fields.map(item => item.isRent ?? false)) } {fields.map((field, index) => ( <Collapse key={field.key} defaultActiveKey={[field.key]}> <Panel header={`房产信息 ${index + 1}`}> <Form.Item {...field} name={[field.name, 'type']} label="房产类型" rules={[{ required: true }]} > <Select value={rentStates[index] ? 'rent' : 'own'} onChange={(value) => { const newStates = [...rentStates]; newStates[index] = value === 'rent'; setRentStates(newStates); }} > <Select.Option value="own">自有</Select.Option> <Select.Option value="rent">租赁</Select.Option> </Select> </Form.Item> {/* 根据当前索引的状态渲染租金输入框 */} {rentStates[index] && ( <Form.Item name={[field.name, 'rent']} label="月租金" rules={[{ required: true, message: '请输入月租金' }]} > <InputNumber min={0} style={{ width: '100%' }} /> </Form.Item> )} <Button type="dashed" onClick={() => { remove(field.name); // 删除对应索引的状态 const newStates = [...rentStates]; newStates.splice(index, 1); setRentStates(newStates); }} > 删除 </Button> </Panel> </Collapse> ))} <Button type="primary" onClick={() => { add(); // 添加新面板时追加默认状态 setRentStates([...rentStates, false]); }} style={{ marginTop: 16 }} > 添加房产 </Button> </> )} </Form.List> </Form> ); }; export default DynamicForm;
注意事项
- 需要手动同步状态数组与
Form.List的fields长度,避免索引错位 - 删除面板时要同步移除对应索引的状态值
内容的提问来源于stack exchange,提问作者someone
相关产品推荐
相关产品推荐

