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

如何解决React点击时元素重复生成、函数执行两次的问题

问题根因
  • 异步查询和DOM生成逻辑错误放在render函数中:React的render函数会在每次状态更新时重新执行,你在render内调用waitForData触发Firestore查询,每次render都会触发一次数据拉取和后续DOM生成逻辑,直接导致重复执行。
  • 违背React设计逻辑使用原生DOM操作:你没有通过React状态管理渲染列表,而是直接用原生API创建、插入DOM,React无法感知这部分DOM的存在,状态更新重渲染时会重复生成节点。同时循环生成的元素都使用相同idwriteto,违反id唯一性规则,导致DOM查询和赋值逻辑异常,新增的节点没有被赋值出现空白。
  • 新增节点前未清空容器:每次调用getfiles都直接向.filtered-docs追加节点,没有清空之前的内容,点击次数越多生成的冗余节点越多。
  • 新增数据后触发的逻辑链重复:点击按钮触发createFileToDb写入数据后,触发组件状态更新重渲染,render内的查询逻辑又会执行一次,重复生成节点。
修复步骤
  1. 把所有异步查询逻辑从render函数中移出,放到组件生命周期或事件回调中:组件挂载时(componentDidMount)执行一次初始数据拉取,新增文档成功后在createFileToDb的then回调中主动拉取一次数据即可,不要在render中做任何副作用操作。
  2. 用React状态管理列表渲染,完全去掉原生DOM操作:在state中新增documentList字段存储从Firestore拿到的文档数据,查询到数据后调用setState更新状态,render函数中直接遍历documentList生成对应的DOM节点,不要手动调用createElement、appendChild等API。
  3. 去掉重复id,循环生成的元素改用唯一key标识,或者用class替代id做标识。
  4. 表单取值改用React受控组件实现,不要用getElementById从DOM中拿值,避免直接操作DOM的各种异常。

如果暂时不想改造为React状态渲染的方式,每次调用getfiles生成节点前,先清空容器的原有内容,新增代码document.querySelector('.filtered-docs').innerHTML = '';即可解决重复追加的问题,但还是推荐使用React官方的渲染方式避免后续更多异常。

改造示例代码
class DocumentComponent extends React.Component {
  // 初始化state,存储表单值和文档列表
  state = {
    documentList: [],
    docName: '',
    specifiedWidth: '',
    specifiedHeight: '',
    colorType: ''
  }

  componentDidMount() {
    // 组件挂载后拉取初始文档列表
    this.loadDocumentList();
  }

  // 封装统一的文档拉取方法
  loadDocumentList = async () => {
    const list = await this.getDataFromFirebase();
    this.setState({ documentList: list });
  }

  // Firebase拉取数据逻辑
  getDataFromFirebase = () => {
    return firebase.firestore()
      .collection("documents")
      .doc(firebase.auth().currentUser.uid)
      .collection("userDocs")
      .orderBy("creation", "asc")
      .get()
      .then(snapshot => {
        return snapshot.docs.map(doc => ({
          id: doc.id,
          data: doc.data()
        }));
      }).catch(err => console.error('拉取数据失败:', err));
  }

  // 写入Firebase逻辑
  createFileToDb = () => {
    const { docName, specifiedWidth, specifiedHeight, colorType } = this.state;
    firebase.firestore().collection("documents")
      .doc(firebase.auth().currentUser.uid)
      .collection("userDocs")
      .add({
        documentName: docName,
        width: Number(specifiedWidth),
        height: Number(specifiedHeight),
        docColorType: colorType,
        creation: firebase.firestore.FieldValue.serverTimestamp()
      }).then(() => {
        console.log("文件写入数据库成功");
        // 写入成功后重新拉取列表刷新页面
        this.loadDocumentList();
      }).catch(() => {
        console.log("文件写入失败");
      })
  }

  // 表单输入绑定state,实现受控组件
  handleInputChange = (e) => {
    this.setState({
      [e.target.id]: e.target.value
    });
  }

  render() {
    const { documentList } = this.state;
    return (
      <div>
        {/* 表单区域 */}
        <input 
          id="docName" 
          value={this.state.docName} 
          onChange={this.handleInputChange} 
          placeholder="自定义标题" 
        />
        <input 
          id="specifiedWidth" 
          value={this.state.specifiedWidth} 
          onChange={this.handleInputChange} 
          placeholder="文档宽度" 
        />
        <input 
          id="specifiedHeight" 
          value={this.state.specifiedHeight} 
          onChange={this.handleInputChange} 
          placeholder="文档高度" 
        />
        <select 
          id="colorType" 
          value={this.state.colorType} 
          onChange={this.handleInputChange}
        >
          <option value="light">浅色</option>
          <option value="dark">深色</option>
        </select>
        <button onClick={this.createFileToDb}>创建文档</button>

        {/* 文档列表渲染区域,完全通过state生成,不需要手动操作DOM */}
        <div className="filtered-docs">
          {documentList.map(docItem => (
            <div key={docItem.id} className="result">
              <p className="writeto">{docItem.data.documentName}</p>
            </div>
          ))}
        </div>
      </div>
    )
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 14:09:03