如何将包含 HTML 的字符串解析为 React 组件
我正在使用这个库:
html-to-react
,因为我发现可以将 HTML 作为字符串“编译”到 React 组件中。
我们正在做一个基于小部件的系统,它们可能会随着时间而改变(即添加/删除/更新/等),所以我们计划将小部件 HTML 从数据库发送到前端,这是使用 React 完成的。
我已经成功地使用 HTML 完成了一个简单的小部件,现在我正在尝试这个小部件来响应点击事件,所以我考虑从 HTML 添加
onclick=method()
方法或从 React 添加
onClick={method
。但是我无法获得所需的输出,因为我在浏览器的控制台中收到这些错误。
Warning: Invalid event handler property `onclick`. React events use the camelCase naming convention, for example `onClick`.
in label
in span
in div
Warning: Invalid event handler property `onclick`. Did you mean `onClick`?
in label
in span
in div
in DireccionComponent (at App.js:51)
in div (at App.js:50)
in GridItem (created by ReactGridLayout)
in div (created by ReactGridLayout)
in ReactGridLayout (at App.js:49)
in App (at index.js:11)
我使用的代码如下:
App.js
import React, { Component } from 'react';
import './App.css';
import DireccionComponent from './DireccionComponent.js';
class App extends Component {
render() {
return (
<DireccionComponent />
);
}
}
export default App;
DireccionComponent.js
import React, {Component} from 'react';
var ReactDOMServer = require('react-dom/server');
var HtmlToReactParser = require('html-to-react').Parser;
let htmlInput = ''
+ '<div>'
+ '<center><label className="title">Widget</label></center>'
+ '<span className="ui-float-label">'
+ '<label htmlFor="float-input" onClick={this.myFunction}>Hello</label>'
+ '</span>'
+ '</div>';
var htmlToReactParser = new HtmlToReactParser();
var reactElement = htmlToReactParser.parse(htmlInput);
var reactHtml = ReactDOMServer.renderToStaticMarkup(reactElement);
class DireccionComponent extends Component {
constructor(props) {
super (props);
this.myFunction = this.myFunction.bind(this);
}
render() {
return (
reactElement
)
}
myFunction() {
console.log('Hola');
alert("Hola");
}
}
export default DireccionComponent;
package.json
{
"name": "my-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"html-to-react": "^1.3.3",
"primereact": "^1.5.1",
"react": "^16.3.2",
"react-dom": "^16.3.2",
"react-dom-server": "0.0.5",
"react-grid-layout": "^0.16.6",
"react-scripts": "1.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
是否可以使用上述库将 HTML 解析为 React Components 来获取
alert
或类似内容?如果可以,我该怎么做?或者我做错了什么?
编辑
不一定非要我使用的库,如果您使用过另一个库并做过类似的事情,我很乐意接受这些建议(请注意,我不是在问软件或库,而是如何在包含我的 HTML 的字符串中调用
onClick
方法或解决方法)
编辑 2
尝试后,我发现删除此行:
var reactHtml = ReactDOMServer.renderToStaticMarkup(reactElement);
删除其中一条消息。库使用该元素来测试当前 HTML 和解析的 HTML 是否相同。在我的情况下我不需要它,但控制台中仍然会出现当前错误:
Warning: Invalid event handler property `onclick`. Did you mean `onClick`?
in label
in span
in div
in DireccionComponent (at App.js:47)
in App (at index.js:11)
编辑 3
经过一番调查,我在
dangerousSetInnerHTML
中找到了
这个答案
然后我尝试按照
这个答案
在我的 HTML 中放置一个
alert
,如下所示:
'<label htmlFor="float-input" onclick="alert(\'Hello\')">Hello2</label>'
并且它触发了
alert
,但是如果我像下面的代码一样声明
myFunction
(请注意,我试图在类外部、内部将其声明为
function
,并将其声明为
var myFunction = function() { ... }
,全部一起并分开):
import React, {Component} from 'react';
var ReactDOMServer = require('react-dom/server');
var HtmlToReactParser = require('html-to-react').Parser;
let htmlInput = ''
//+ '<div>'
+ '<center><label className="title">Widget</label></center>'
+ '<span className="ui-float-label">'
+ '<label htmlFor="float-input" onclick="myFunction()">Hello2</label>'
+ '</span>'
//+ '</div>';
var htmlToReactParser = new HtmlToReactParser();
var reactElement = htmlToReactParser.parse(htmlInput);
class DireccionComponent extends Component {
constructor(props) {
super (props);
this.myFunction = this.myFunction.bind(this);
}
render() {
return (
<div dangerouslySetInnerHTML={{__html: htmlInput}}>
</div>
)
}
myFunction() {
console.log('Hola');
alert("Hola");
}
}
function myFunction() {
console.log('Hola');
alert("Hola");
}
export default DireccionComponent;
但是这次我收到一个新的错误:
ReferenceError: myFunction is not defined[Learn More]
我发现了一个类似的问题:
处理 <a> 的 onClick标记内的 dangerouslySetInnerHTML/regular html
存在此问题,并尝试进行进一步调查,发现
此评论
,其中说
dangerouslySetInnerHTML
中的事件不会以这种方式触发,并链接到
文档
。
因此,虽然当我直接从
onclick
方法编写时收到
alert
时这似乎是一种解决方法,或者可能是我没有以正确的方式执行此操作,因此如果有更多经验的人可以尝试并澄清,那就太好了。
编辑 4
我找到了一种以这种方式显示
alert
的方法:
- 将 HTML 编写为字符串
-
通过
dangerouslySetInnerHTML
设置 HTML> -
在 React 的
componentDidMound()
函数上使用纯 Javascript 向其添加onclick
函数。 - 成功
但是, 我们试图做的是 :
-
创建一些方法,例如:
addTwoNumbers
或类似的东西 -
从 HTML 字符串中发送
onclick
或onClick
来调用addTwoNumbers
函数。 -
每次我们需要添加需要使用
addTwoNumbers
方法的新组件时都重复此操作,只需为其编写 HTML,将其保存在数据库中并检索它,然后使用它即可。
类似于:
...
<label onClick={myFunction}> My Label </label>
...
或者如我上面所说:
...
<label onClick={addTwoNumbers}> My Label </label>
...
允许我获取
alert
的代码如下:
import React, {Component} from 'react';
import Parser from 'html-react-parser';
import {render} from 'react-dom';
var ReactDOMServer = require('react-dom/server');
var HtmlToReactParser = require('html-to-react').Parser;
let htmlInput = ''
//+ '<div>'
+ '<center><label className="title">Widget</label></center>'
+ '<span className="ui-float-label">'
+ '<label htmlFor="float-input" id="myLabel">Hello2</label>'
+ '</span>'
//+ '</div>';
var htmlToReactParser = new HtmlToReactParser();
var reactElement = htmlToReactParser.parse(htmlInput);
class DireccionComponent extends Component {
constructor(props) {
super (props);
this.myFunction = this.myFunction.bind(this);
}
render() {
return (
<div dangerouslySetInnerHTML={{__html: htmlInput}}>
</div>
)
}
componentDidMount() {
console.log('DONE');
let myLabel = document.getElementById("myLabel");
myLabel.onclick = function() {
alert();
console.log('clicked');
}
console.log(myLabel);
}
myFunction() {
console.log('Hola');
alert("Hola");
}
}
function myFunction() {
console.log('Hola');
alert("Hola");
}
export default DireccionComponent;
考虑到这一点,您知道还有其他方法可以完成我想做的事情吗?
该解决方案有点黑客,复制粘贴到 codesandbox
class App extends Component {
constructor(props) {
super(props);
this.myfunc = this.myfunc.bind(this);
}
componentDidMount() {
window.myfunc = this.myfunc;
}
myfunc() {
console.log("from myfunc");
}
componentWillUnmount() {
window.myfunc = null;
}
render() {
let inputhtml = "<a onclick=window.myfunc()>HelloWorld</a>";
return (
<div style={styles}>
<div dangerouslySetInnerHTML={{ __html: inputhtml }} />
</div>
);
}
}
我不知道它是否适用于您的特定情况,但一个想法是根本不解析 HTML。您可以告诉 webpack 为不同的文件集生成单独的包。现在,我从未这样做过,但我读过几次您可以这样做;例如, 这个 。
然后,您可以拥有一个 main.bundle.js,其中包含您网站的所有不变的部分。此外,为每个可以更改的小部件创建一个 widget1.bundle.js 等。在您的 index.html 上导入所有这些。然后,将您的网络服务器配置为从不缓存较小的包(widget1 等)。每当用户进入网站时,它都会再次下载这些小部件,从而有效地更新它们。如果您想要更加花哨,您可以为每个小部件的当前版本提供一个 API,并在小部件包上添加一个带有版本的额外字段,这样您就可以定期检查组件是否是最新的,以防用户有一段时间没有重新加载页面。如果您发现过时的组件,只需强制重新加载,浏览器就会确保获取最新版本。
我知道这与您要走的路完全不同,但如果它适合您的特定情况,我相信它会更简单、更容易实现和保证稳定性、更易于维护,总体而言,比手动解析 html 并自行处理所有请求更好。
为什么不使用浏览器 DOMParser API 将您的 HTML 解析为屏幕外/分离的 DOM,然后遍历该 DOM 并使用
React.createElement()
来构建 React 的 DOM?类似
let str = "... your HTML ..."
function traverse(node) {
let children = []
for (let i = 0; i < node.children.length; i++)
children.push(traverse(node.children.item(i)))
return React.createElement(node.localName, null, children)
// or maybe use the following instead (React's API doc
// isn't very clear about this):
// return React.createElement(node.localName, null, ...children)
}
let reactElementForStr =
traverse(new DOMParser().parseFromString(str, 'text/html'))
请注意,我对 React 的内部工作原理只有一知半解,而且根本没有测试过。