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

React中实现点击+添加、X删除同款Div的问题咨询

解决方案

问题分析

原代码的核心问题:

  • handleAdd 仅返回 <IpRangeInput /> 组件,但未将其加入实际渲染列表
  • 删除按钮错误绑定了添加函数,无删除逻辑
  • 每个输入组件独立管理自身状态,无法统一控制多输入项的增删

修正后的代码

通过状态提升将所有输入项的状态统一交给父组件管理,用数组存储输入项信息,父组件负责增删操作,子组件仅处理自身渲染和值更新。

完整代码实现

import { useState } from 'react';
import { Box, ThemeProvider, FormControl, Select, MenuItem, TextField, IconButton } from '@mui/material';
import AddIcon from '@mui/icons-material/Add';
import ClearIcon from '@mui/icons-material/Clear';
// 请替换为你的theme和styles路径
import theme from './your-theme-path';
import styles from './your-styles-path';

// 定义单个输入项的状态类型
type IpItem = {
  id: string;
  type: 'IP/CIDR' | 'Range' | '';
  ipValue: string;
  fromValue: string;
  toValue: string;
};

// 父组件:管理所有输入项的增删和状态
function IpRangeInputList() {
  // 初始化一个默认输入项
  const [ipItems, setIpItems] = useState<IpItem[]>([{
    id: Date.now().toString(),
    type: '',
    ipValue: '',
    fromValue: '',
    toValue: ''
  }]);

  // 添加新输入项
  const handleAdd = () => {
    setIpItems(prev => [...prev, {
      id: Date.now().toString(),
      type: '',
      ipValue: '',
      fromValue: '',
      toValue: ''
    }]);
  };

  // 删除指定输入项(至少保留一个)
  const handleDelete = (id: string) => {
    if (ipItems.length > 1) {
      setIpItems(prev => prev.filter(item => item.id !== id));
    }
  };

  // 更新单个输入项的状态
  const updateIpItem = (id: string, updates: Partial<IpItem>) => {
    setIpItems(prev => prev.map(item => 
      item.id === id ? {...item, ...updates} : item
    ));
  };

  return (
    <div>
      {ipItems.map(item => (
        <IpRangeInput 
          key={item.id}
          item={item}
          onUpdate={(updates) => updateIpItem(item.id, updates)}
          onDelete={() => handleDelete(item.id)}
        />
      ))}
      <IconButton onClick={handleAdd} style={{marginTop: '16px'}}>
        <AddIcon />
      </IconButton>
    </div>
  );
}

// 子组件:负责单个输入项的渲染和交互
function IpRangeInput({ item, onUpdate, onDelete }) {
  return (
    <Box className={styles.inputContainer}>
      <ThemeProvider theme={theme}>
        <FormControl className={styles.inputContainer__select}>
          <Select
            defaultValue="Select"
            id="select-label"
            value={item.type}
            onChange={(e) => onUpdate({type: e.target.value as 'IP/CIDR' | 'Range'})}
          >
            <MenuItem value="IP/CIDR">IP / CIDR</MenuItem>
            <MenuItem value="Range">Range</MenuItem>
          </Select>
        </FormControl>

        {item.type === 'Range' ? (
          <>
            <span className={styles.inputContainer__text}>From</span>
            <Box
              className={[
                styles.inputContainer__input,
                styles.inputContainer__newInput,
              ].join(' ')}
            >
              <TextField
                value={item.fromValue}
                onChange={(e) => onUpdate({fromValue: e.target.value})}
              />
            </Box>

            <span className={styles.inputContainer__text}>To</span>
            <Box
              className={[
                styles.inputContainer__input,
                styles.inputContainer__newInput,
              ].join(' ')}
            >
              <TextField
                value={item.toValue}
                onChange={(e) => onUpdate({toValue: e.target.value})}
              />
            </Box>
          </>
        ) : (
          <Box className={styles.inputContainer__input}>
            <TextField 
              value={item.ipValue} 
              onChange={(e) => onUpdate({ipValue: e.target.value})} 
            />
          </Box>
        )}

        <IconButton
          className={styles.inputContainer__clearButton}
          onClick={onDelete}
        >
          <ClearIcon />
        </IconButton>
      </ThemeProvider>
    </Box>
  );
}

export default IpRangeInputList;

关键改动说明

  • 状态统一管理:父组件用数组存储所有输入项状态,避免子组件独立状态导致的增删失控
  • 唯一标识:每个输入项添加id作为React列表渲染的唯一key,保证渲染稳定性
  • 增删逻辑:handleAdd向数组追加新输入项;handleDelete通过id过滤删除目标项,同时限制至少保留一个输入项
  • 子组件通信:通过props传递更新和删除函数,子组件仅负责触发操作,不管理全局状态
  • 修正绑定错误:删除按钮绑定正确的删除函数,解决原代码中绑定错误的问题

内容的提问来源于stack exchange,提问作者Stiven Ballshi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 21:35:14