You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 12:30:53