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

ReactJS+Firebase表格编辑功能仅首行生效问题求助

问题原因分析
  1. 重复渲染Modal且共用状态:在internships.map循环里为每一行都渲染了一个Modal,所有Modal共用同一个openEdit状态。点击任意编辑按钮时,所有Modal都会被触发显示,但由于DOM层级关系,只有第一个Modal会被用户看到,导致看起来只有第一条数据能编辑。
  2. key值不唯一:TableRow的key使用了internships.company,如果存在同名公司,会导致React无法正确区分不同行,引发渲染异常。
  3. 全局更新状态混乱:updatedCompany等更新状态是全局的,点击不同行编辑时,输入框的默认值和状态会相互干扰,导致编辑内容错误。
修复方案
  1. 将Modal移出循环,只保留一个全局Modal。
  2. 添加currentEditInternship状态,存储当前要编辑的实习数据(包含id和初始字段值)。
  3. 点击编辑按钮时,将当前行的数据存入currentEditInternship,并打开Modal。
  4. 使用internships.id作为TableRow的唯一key。
  5. 更新时使用currentEditInternship.id定位要修改的Firebase文档。
修改后的完整代码
//import statements

const InternshipsWidget = () => {
  const [internships, setInternships] = useState([]);
  const internshipsCollectionRef = collection(db, "internships")
  const [newCompany, setNewCompany] = useState("");
  const [newField, setNewField] = useState("");
  const [newSetup, setNewSetup] = useState("");
  const [open, setOpen] = useState(false);
  const [openEdit, setOpenEdit] = useState(false);

  // 新增:存储当前编辑的实习数据
  const [currentEditInternship, setCurrentEditInternship] = useState(null);

  //updated states:改为基于当前编辑数据初始化
  const [updatedCompany, setUpdatedCompany] = useState("");
  const [updatedField, setUpdatedField] = useState("");
  const [updatedSetup, setUpdatedSetup] = useState("");

  const handleOpen = () => {
    setOpen(true);
  };

  const handleClose = () => {
    setOpen(false);
  };

  // 修改:打开编辑Modal时,设置当前编辑数据并初始化输入框值
  const handleOpenEdit = (internship) => {
    setCurrentEditInternship(internship);
    setUpdatedCompany(internship.Company);
    setUpdatedField(internship.Field);
    setUpdatedSetup(internship.Setup);
    setOpenEdit(true);
  };

  const handleCloseEdit = () => {
    setOpenEdit(false);
    setCurrentEditInternship(null); // 关闭时清空当前编辑数据
  };

  const deleteUser = async (id) => {
    const userDoc = doc(db, "internships", id);
    await deleteDoc(userDoc);
  }

  useEffect(() => {
    const unsubscribe = onSnapshot(internshipsCollectionRef, (snapshot) => {
      setInternships(
        snapshot.docs.map((doc) => ({ ...doc.data(), id: doc.id }))
      );
    });
    return unsubscribe;
  }, []);

  const createUser = async () => {
    await addDoc(internshipsCollectionRef, { Company: newCompany, Field: newField, Setup: newSetup});
  };
  
  const user = useSelector((state) => state.user);

  const updateUser = async () => {
    if (!currentEditInternship) return;
    const userDoc = doc(db, "internships", currentEditInternship.id);
    await updateDoc(userDoc, {
      Company: updatedCompany, 
      Field: updatedField, 
      Setup: updatedSetup
    });
    handleCloseEdit();
  };

  return (
    <motion.Box>  
      <Box>
        <TableContainer component={Paper}>
          <Table aria-label="Calendar table">
            <TableHead>
              <TableRow >
                <TableCell align="left">Company Name</TableCell>
                <TableCell align="left">Field</TableCell>
                <TableCell colSpan={2} align="left">Work Setup</TableCell>
              </TableRow>
            </TableHead>
            <TableBody>
              {internships.map((internship) => (
                <TableRow colSpan={5} key={internship.id}> {/* 修改:用id作为唯一key */}
                  <TableCell align="left">
                    <Typography>{internship.Company}</Typography>
                  </TableCell>
                  <TableCell align="left">
                    <Typography>{internship.Field}</Typography>
                  </TableCell>
                  <TableCell align="left">
                    <Typography>{internship.Setup}</Typography>
                  </TableCell>
                  {user.isAdmin && (
                    <TableCell align="left">
                      {/* 修改:点击编辑时传入当前实习数据 */}
                      <IconButton onClick={() => handleOpenEdit(internship)}><EditIcon/></IconButton>
                      <IconButton onClick={() => deleteUser(internship.id)}>
                        <DeleteIcon/>
                      </IconButton>
                    </TableCell>
                  )}
                </TableRow>
              ))}
            </TableBody>
          </Table>
        </TableContainer>
      </Box>

      {/* 移出循环:全局唯一的编辑Modal */}
      <Modal
        closeAfterTransition
        open={openEdit} 
        onClose={handleCloseEdit}
      >
        <Fade in={openEdit}>
          <Box>
            <IconButton onClick={handleCloseEdit}>
              <CloseRoundedIcon />
            </IconButton>
            <Stack>
              <TextField 
                id="outlined-basic" 
                label="Company" 
                variant="outlined" 
                value={updatedCompany} // 改为受控组件,使用updatedCompany状态
                onChange={(event) => setUpdatedCompany(event.target.value)}
              />
              <TextField 
                id="outlined-basic" 
                label="Field" 
                variant="outlined" 
                value={updatedField}
                onChange={(event) => setUpdatedField(event.target.value)}
              />
              <TextField 
                id="outlined-basic" 
                label="Work Setup" 
                variant="outlined" 
                value={updatedSetup}
                onChange={(event) => setUpdatedSetup(event.target.value)}
              />
              <Button 
                variant="outlined" 
                onClick={updateUser}
              >
                <Typography p={1}>Update Company</Typography>
              </Button>
            </Stack>
          </Box>
        </Fade>
      </Modal>
    </motion.Box>
  );
};

export default InternshipsWidget;
关键修改点说明
  • 移除循环内的Modal:只保留一个全局Modal,避免重复渲染和状态冲突。
  • 新增currentEditInternship状态:存储当前要编辑的行数据,确保更新时能定位到正确的Firebase文档。
  • 修改编辑按钮点击逻辑:点击时传入当前行数据,初始化输入框的状态值。
  • 使用受控组件:输入框从defaultValue改为value绑定状态,确保输入内容和状态同步。
  • 修正key值:用Firebase文档的唯一id作为TableRow的key,避免渲染异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 16:23:06