如何将onclick事件添加到reactjs中由dangerouslysetInnerHtml呈现的字符串?

IT技术 javascript html reactjs onclick innerhtml
2021-05-13 12:03:14

我有一个字符串,即

let string= "Hello <b>Click here</b>";

render() {
return (<div dangerouslySetInnerHTML={this.createMarkup(value)}/>
}

createMarkup = value => { 
        return { __html: value };
 };

我想要的是能够向<b>标签添加一个 onclick 事件,在点击时执行状态操作。

潜在的问题是我有一个函数应该渲染 API 传递的任何内容。API 将发送一个字符串“Money received for order ID 123 ”,或者可以是我无法控制的任何字符串。后来,我得到一个要求,加粗的项目必须是可点击的,以便执行一些操作。我没有任何其他方法可以解决它。

我怎样才能做到这一点?

3个回答

警告:这听起来像是一个X/Y 问题,其中潜在的问题(无论是什么)应该以不同的方式解决,这样您就不必向通过创建的 DOM 元素添加点击处理程序dangerouslySetInnerHTML(理想情况下,所以您不必根本不必通过创建 DOM 元素dangerouslySetInnerHTML)。但是回答您提出的问题:( 您已经阐明了用例;下面的解决方案 #1 适用并且不是不好的做法。)

我不认为你可以直接做到这一点。我能想到的两种解决方案:

  1. 在 上使用委托事件处理程序:在 上div添加单击处理程序div,但仅当单击通过b元素时才执行操作

  2. ref使用 a div,然后将点击处理程序连接到componentDidMountcomponentDidUpdateb在过divquerySelector或类似物找到元素),沿着以下几行:

以下是#1 的示例:

<div onClick={this.clickHandler} dangerouslySetInnerHTML={this.createMarkup(string)}/>

...这里clickHandler

clickHandler(e) {
    // `target` is the element the click was on (the div we hooked or an element
    // with in it), `currentTarget` is the div we hooked the event on
    const el = e.target.closest("B");
    if (el && e.currentTarget.contains(el)) {
        // ...do your state change...
    }
}

...或者如果您需要支持旧浏览器而无需ParentNode#closest

clickHandler(e) {
    // `target` is the element the click was on (the div we hooked or an element
    // with in it), `currentTarget` is the div we hooked the event on
    let el = e.target;
    while (el && el !== e.currentTarget && el.tagName !== "B") {
        el = el.parentNode;
    }
    if (el && el.tagName === "B") {
        // ...do your state change...
    }
}

...以及您clickHandler在构造函数中绑定的位置(而不是使用带有箭头函数的属性;为什么:1 , 2):

this.clickHandler = this.clickHandler.bind(this);

现场示例:

这是 #2 的示例,但如果 A) 您可以单独解决潜在问题,或者 B) #1 有效,则不要这样做:

Refs是一个“逃生舱”,让您可以直接访问 DOM。不要轻易使用 refs;通常,有更好的选择。

但再说一次:我会以不同的方式解决根本问题,无论它是什么。

react-html-parser可以将 HTML 字符串转换为 React 组件。

使用转换回调函数,您可以使用 JSX 标记更新 HTML 字符串中的任何标记,添加任何属性和事件侦听器。

这是我使用它的方式:

ReactHtmlParser(item.value, {
    transform: (node) => {
        if (node.name === 'a' && node.attribs && node.attribs.href) {
            const matched = node.attribs.href.match(/^activity\/([0-9]+)$/i);

            if (matched && matched[1]) { // activity id
                return <a
                        href={node.attribs.href}
                        onClick={(e) => {                                                            
                          e.preventDefault();                                                            
                          this.props.openActivityModal(matched[1]);
                        }}
                        >{node.children[0].data}</a>
            }
        }
    } 
})

这是满足您需求的干净方法。通过根据<br>标签拆分字符串,您最终可以得到一个可映射的文本数组:

class BoldText extends React.Component {
	constructor(props) {
		super(props)

		this.state = {
			input: "Money received for order ID <b>123</b>, wow for real, so <b>cool</b> its insane"
		}
	}

	boldClick = ev => {
		console.log('clicked !')
	}

	render() {
		const { input } = this.state

		const a = input.split('</b>')


		const filter = /<b>.*<\/b>/
		const text = input.split(filter)
		const clickable = filter.exec(input)
//<b onClick={this.boldClick}></b>
		return (
			<div>
				<p>{a.map(t => {
					const [text, bold] = t.split('<b>')
					return <span>{text}<b onClick={this.boldClick}>{bold}</b></span>
				})}
				</p>
			</div>
		)
	}
}

ReactDOM.render(<BoldText />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.production.min.js"></script>
<idv id='root'>

此解决方案应该可以解决您在上述答案的评论中提到的问题。您可以将 API 调用放在componentDidMount生命周期函数中并从那里更改状态。