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

React中动态渲染HTML表格及修复删除按钮失效问题

解决方案:放弃模板字符串,用React JSX直接渲染表格

你的问题根源在于用模板字符串拼接HTML的方式完全违背了React的设计模式,既导致事件绑定失效,又降低了渲染效率。以下是彻底的解决办法:

问题分析

  1. "async is not defined"错误原因:模板字符串会把deleteTodo函数转成字符串,比如如果它是async函数,最终HTML里会变成onClick=async function(...)。浏览器解析时会把async当成未定义的全局变量,因此报错。
  2. 模板字符串的缺陷:每次更新都要重新拼接整个HTML字符串,React无法做局部DOM更新;同时dangerouslySetInnerHTML存在XSS风险,代码可读性也极差。

最优实现方案

用React的JSX直接渲染表格,利用数组map生成表行,完全抛弃字符串拼接和dangerouslySetInnerHTML。这样既能正确绑定事件,又能利用React虚拟DOM的高效diff算法。

完整代码示例

import { useState } from 'react';

function TodoTable() {
  // 替换成你实际的状态/接口数据
  const [todos, setTodos] = useState([
    { id: 1, title: '早起锻炼' },
    { id: 2, title: '每日阅读' }
  ]);
  const [todoLogs, setTodoLogs] = useState([
    { id: 1, d09_04_24: true, d08_04_24: false },
    { id: 2, d09_04_24: false, d08_04_24: true }
  ]);

  // 异步删除函数,现在可以正常工作
  const deleteTodo = async (todoId) => {
    try {
      // 这里写实际的删除逻辑,比如调用后端API
      // await fetch(`/api/todos/${todoId}`, { method: 'DELETE' });
      
      // 更新前端状态
      setTodos(prev => prev.filter(todo => todo.id !== todoId));
      setTodoLogs(prev => prev.filter(log => log.id !== todoId));
    } catch (err) {
      console.error('删除失败:', err);
    }
  };

  return (
    <table className='mt-3 border-2 [&>tr]:border-2 [&>tr>td]:border-2 [&>tr>th]:border-2 [&>tr>th]:px-1 text-center'>
      {/* 表头直接用JSX渲染 */}
      <thead>
        <tr className='border-2 border-black ml-2'>
          <th>Habit</th>
          <th>09/04</th>
          <th>08/04</th>
          <th>操作</th>
        </tr>
      </thead>
      {/* 表体用map遍历生成每一行 */}
      <tbody>
        {todoLogs.map(todoLog => {
          const targetTodo = todos.find(todo => todo.id === todoLog.id);
          // 处理找不到对应todo的边界情况
          if (!targetTodo) return null;

          return (
            <tr key={todoLog.id}> {/* 必须添加唯一key,提升渲染性能 */}
              <td>{targetTodo.title}</td>
              <td>
                <input
                  type="checkbox"
                  checked={todoLog.d09_04_24}
                  {/* 可选:添加状态切换逻辑 */}
                  onChange={() => {
                    setTodoLogs(prev => prev.map(log => 
                      log.id === todoLog.id ? {...log, d09_04_24: !log.d09_04_24} : log
                    ));
                  }}
                />
              </td>
              <td>
                <input
                  type="checkbox"
                  checked={todoLog.d08_04_24}
                  onChange={() => {
                    setTodoLogs(prev => prev.map(log => 
                      log.id === todoLog.id ? {...log, d08_04_24: !log.d08_04_24} : log
                    ));
                  }}
                />
              </td>
              <td>
                <button onClick={() => deleteTodo(todoLog.id)}>
                  <i className="material-icons">delete</i>
                </button>
              </td>
            </tr>
          );
        })}
      </tbody>
    </table>
  );
}

export default TodoTable;

方案优势

  • 事件绑定正常:直接传递函数引用,async函数可以正常执行,不会出现变量未定义错误。
  • 渲染效率更高:React只会更新状态变化对应的DOM节点,无需重绘整个表格。
  • 代码可维护性强:JSX结构清晰,逻辑与视图分离,便于调试和扩展。
  • 安全性提升:避免了dangerouslySetInnerHTML带来的XSS攻击风险。

关于你尝试过的方法

useRef、innerHTML这类直接操作DOM的方式不符合React的状态驱动视图设计理念,不推荐用于渲染列表。React的核心是用状态变化驱动视图更新,所有DOM操作都应该通过状态更新间接完成。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 23:14:51