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

React中通过函数构建HTML时onChange事件失效问题求解

React中dangerouslySetInnerHTML生成的下拉框onChange事件不触发问题解决

问题描述

在React组件中,通过字符串拼接生成包含下拉框的筛选器HTML代码,使用dangerouslySetInnerHTML插入DOM后,选中下拉选项时handleChange函数完全无响应。查看浏览器生成的HTML发现,onChange属性中是函数的字符串字面量,而非实际绑定的事件处理函数。

问题原因

dangerouslySetInnerHTML的作用是直接将字符串作为原始HTML插入DOM,React不会对其中的事件属性做任何处理——你传入的handleChange函数被自动转成了字符串,浏览器只会把它当成普通文本,无法识别为可执行的事件处理逻辑。这种方式不仅违背了React的声明式编程范式,还存在XSS安全风险。

解决方案

方案一:用JSX代替字符串拼接(推荐)

放弃字符串拼接HTML的方式,直接返回React元素(JSX),让React正常处理事件绑定。

修改后的核心代码:

const My_Component = (props) => {
  // ... 其他状态、函数不变

  function handleChange(event) {
    const VALUE = event.target.value;
    console.log('VALUE=' + JSON.stringify(VALUE));            
  }

  // 改为返回JSX元素
  const build_CodiceHtml = (handleChange) => {
    return (
      <select 
        onChange={handleChange} 
        name="MY_NAME" 
        value="MY_VALUE" 
        size="sm" 
        className="text-center"
      >
        <option value="1">AAAAA</option>
        <option value="2">BBBBB</option>
        <option value="3">CCCCC</option>
      </select>
    );
  }

  // 同样返回JSX元素
  const getTableFiltri = () => {
    return (
      <table 
        cellpadding="20" 
        style={{
          border: '1px solid black',
          marginLeft: 'auto',
          marginRight: 'auto'
        }}
      >
        <tbody>
          <tr className="justify-content-md-center">
            <td>{build_CodiceHtml(handleChange)}</td>
          </tr>
        </tbody>
      </table>
    );
  }

  // ... getTableWithValue函数不变

  return (
    <div>
      <Modal show={props.isOpen} size="xl">
        {/* ... 其他部分不变 */}
        <Modal.Body className="modalBody">
          {/* 直接渲染JSX,不再使用dangerouslySetInnerHTML */}
          {getTableFiltri()}
          {getTableWithValue()}
        </Modal.Body>
        {/* ... 其他部分不变 */}
      </Modal>
    </div>
  )
}

这种方式完全符合React的开发规范,事件能正常触发,同时避免了XSS风险。

方案二:手动绑定事件(仅用于特殊场景)

如果因业务算法限制必须用字符串拼接HTML,可以在DOM插入后手动绑定事件:

import { useEffect, useRef } from 'react';

const My_Component = (props) => {
  const filterContainerRef = useRef(null);

  function handleChange(event) {
    const VALUE = event.target.value;
    console.log('VALUE=' + JSON.stringify(VALUE));            
  }

  const build_CodiceHtml = () => {
    // 给select添加唯一id
    let HTML_CODE= '<select id="filter-select" name="MY_NAME" value="MY_VALUE" size="sm" className="text-center"  >';
    HTML_CODE=HTML_CODE + '<option value="1">AAAAA</option>';
    HTML_CODE=HTML_CODE + '<option value="2">BBBBB</option>';
    HTML_CODE=HTML_CODE + '<option value="3">CCCCC</option>';
    HTML_CODE=HTML_CODE + '</select>';
    return HTML_CODE;
  }

  const getTableFiltri = () => {
    let result=''; 
    result = result + '<table cellpadding="20" style="border:1px solid black;margin-left:auto;margin-right:auto"><tbody>';
    result = result + '<tr className="justify-content-md-center"  ><td>';
    result = result + build_CodiceHtml();
    result = result + '</td></tr>';
    result = result + '</tbody></table>';
    return result;
  }

  // 当modal打开后,手动绑定事件
  useEffect(() => {
    if (props.isOpen) {
      const selectElement = document.getElementById('filter-select');
      if (selectElement) {
        selectElement.addEventListener('change', handleChange);
        // 组件卸载或modal关闭时清理事件
        return () => {
          selectElement.removeEventListener('change', handleChange);
        };
      }
    }
  }, [props.isOpen]);

  // ... 其他代码不变

  return (
    <div>
      <Modal show={props.isOpen} size="xl">
        {/* ... 其他部分不变 */}
        <Modal.Body className="modalBody">
          <div ref={filterContainerRef} dangerouslySetInnerHTML={{ __html: getTableFiltri() }} />
          {getTableWithValue()}
        </Modal.Body>
        {/* ... 其他部分不变 */}
      </Modal>
    </div>
  )
}

注意:这种方式需要手动管理事件的绑定与解绑,容易引发内存泄漏,且存在XSS风险,仅在无法使用JSX的特殊场景下使用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 05:55:29