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

React单选按钮选中后点击上传按钮Replace、Remove函数未触发问题

问题根因

你的代码主要存在3个逻辑错误,导致Replace、Remove操作看起来没有执行:

  1. 仅Add操作调用了setFiles更新状态,Replace和Remove操作仅计算了结果,没有更新状态也没有将最新数据回传给父组件
  2. 状态设计错误:仅用一个files变量同时存原有业务数据和新上传的Excel数据,上传新文件时直接覆盖了原有数据,导致Replace、Remove的比对逻辑无法命中
  3. React的setState是异步执行的,操作完成后直接读files传给父组件,拿到的永远是更新前的旧值

修复步骤

1. 拆分状态区分原有数据和新上传数据

将单一files状态拆分为两个:

  • existingData:存储原有业务数据
  • uploadedFileData:存储新上传Excel读取到的待处理数据

2. 修正操作函数逻辑

每个操作处理完成后更新状态,返回最新结果直接传给父组件,避免异步state问题

3. 修正Excel多sheet读取逻辑

XLSX.utils.sheet_to_json的第二个参数为配置项,不能直接传第二个sheet,需要分别转换后合并数据

修正后完整代码

import React, { useState } from "react";
import * as XLSX from "xlsx";
import { makeStyles } from '@material-ui/core/styles';
import Radio from '@material-ui/core/Radio';
import RadioGroup from '@material-ui/core/RadioGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import FormControl from '@material-ui/core/FormControl';
import Button from '@material-ui/core/Button';

const Upload = (props) => {
  const [value, setValue] = useState('');
  const [error, setError] = useState(false);
  const [helperText, setHelperText] = useState('')
  // 拆分状态:原有业务数据
  const [existingData, setExistingData] = useState([]);
  // 拆分状态:新上传的Excel数据
  const [uploadedFileData, setUploadedFileData] = useState([]);
  const [fileName, setFileName] = useState([]);

  const useStyles = makeStyles((theme) => ({
    formControl: { margin: theme.spacing(3), },
    button: { margin: theme.spacing(1, 1, 0, 0), },
  }));

  const { passData } = props;
  const classes = useStyles();

  const handleRadioChange = (event) => {
    console.log(event.target.value,'event')
    setValue(event.target.value);
    setError(false);
  };

  const handleClick = (event) => {
    event.preventDefault();
    handlingOperation();
  }

  // 添加操作:新数据追加到原有数据后
  const addOperation = () => {
    const newData = [...existingData, ...uploadedFileData]
    setExistingData(newData)
    return newData
  }
     
  // 替换操作:同NPI_ID的更新药店名称
  const replaceOperation = () => {
    const results = existingData.map(x => {
      const el = uploadedFileData.find(y => y.NPI_ID === x.NPI_ID);
      if (el) return {
        ...x,
        PHARMACY_NAME: el.PHARMACY_NAME
      };
      return x;
    });
    setExistingData(results)
    return results
  }

  // 删除操作:移除和新数据同NPI_ID的条目
  const removeOperation = () => {
    const removeFileFilter = existingData.filter(x => !uploadedFileData.some(y => y.NPI_ID === x.NPI_ID));
    setExistingData(removeFileFilter)
    return removeFileFilter
  }
   
  const handlingOperation = () => {
    console.log(value, 'value');
    let latestData = existingData
    if (value === 'Add') { 
      latestData = addOperation()
      setHelperText('文件已添加')
    }
    else if (value === 'Replace') {
      latestData = replaceOperation();
      setHelperText('文件已替换')
    }
    else if (value === 'Remove') {
      latestData = removeOperation();
      setHelperText('文件已删除')
    }
    else {
      setHelperText('请选择一个操作选项。');
      setError(true);
    }
    // 直接传处理后的最新值,不依赖异步state
    passData(latestData)
  }

  const addFiles = (newfiles) => {
    newfiles.map((file) => {
      const filereader = new FileReader();
      filereader.readAsArrayBuffer(file);
      filereader.onload = (e) => {
        const bufferArray = e.target.result;
        const wb = XLSX.read(bufferArray, { type: "buffer" });
        const wsname = wb.SheetNames[0];
        const ws = wb.Sheets[wsname];
        let data = XLSX.utils.sheet_to_json(ws);
        // 如果需要读第二个sheet,取消下方注释
        // if(wb.SheetNames.length > 1) {
        //   const ws1 = wb.Sheets[wb.SheetNames[1]];
        //   const data1 = XLSX.utils.sheet_to_json(ws1);
        //   data = [...data, ...data1]
        // }
        // 新上传的数据存到uploadedFileData,不覆盖原有业务数据
        setUploadedFileData(data);
      };
    });
  };

  const inputsHandler = (e) => {
    const file = [...e.target.files];
    addFiles(file);
    setFileName(file);
  }

  return (
    <div className="container-fluid">
      <FormControl component="fieldset" error={error} className={classes.formControl}>

        <div className="title"><div>PHARMACY</div></div>
        <div className="message"> **Message <span className="messageStatus">{helperText}</span> </div>

        <section className="heading">

          <label className="custom-file-input" for="Upload" >
            <div className="combiner">
              <span className="uploadText">Upload file </span>
            </div>
            <div className="combiner selectedFile">
              {fileName[0] !== undefined ?
                <span className="fileName">{fileName[0].name}</span>
                : null
              }
              <input type="file" name="file" id="Upload" className="input-field" accept=".csv, .xlsx,.xls,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
                onChange={inputsHandler}
                multiple
              />
            </div>
          </label>

        </section>

        <div className="radioTitle">Upload a file to:</div>
        <RadioGroup aria-label="quiz" name="quiz" value={value} onChange={handleRadioChange}>
          <FormControlLabel value="Add" control={<Radio />} label="Add" />
          <FormControlLabel value="Replace" control={<Radio />} label="Replace" />
          <FormControlLabel value="Remove" control={<Radio />} label="Remove" />

        </RadioGroup>

        <div className="buttonParent">
          <Button type="button"
            variant="contained"
            color="primary"
            className={classes.button}
            onClick={(e) => { handleClick(e) }} > Upload  </Button>

        </div>
      </FormControl>
    </div>
  );
}
export default Upload;

如果你的原有业务数据是从父组件传递而来,可以直接替换掉existingData状态,从props中读取即可。

内容的提问来源于stack exchange,提问作者Rashmi UI

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 11:06:03