簡體   English   中英

如何將包含HTML的String解析為React Components

[英]How to parse a String containing HTML to React Components

我正在使用這個庫: html-to-react因為我發現可以將HTML“編譯”為一個字符串來反應組件。

我們正在做一個基於小部件的系統,它們可能會隨着時間而改變(即添加/刪除/更新/等),因此我們計划將小部件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"
  }
}

是否可以使用上面的庫來獲取alert或類似內容來將HTML解析為React組件? 如果是的話,我怎么能這樣做? 或者我做錯了什么?


編輯

它不一定是我正在使用的庫,如果有另一個你曾經使用並做過類似的事情,我願意接受這個建議(注意,我不是要求一個軟件或庫,但如何在包含我的HTML或變通方法的String中調用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]

我發現了一個類似的問題:使用此問題處理危險的SetInnerHTML /常規html中的<a>標簽onClick ,並嘗試進行更多調查並發現此評論說明來自dangerouslySetInnerHTML的事件不會以這種方式觸發和鏈接到文檔

所以,雖然當我直接從onclick方法編寫alert時,這似乎是一種解決方法,或者我可能沒有以正確的方式執行,所以如果有經驗的人可以嘗試並澄清,那將是很好的。


編輯4

我找到了一種以這種方式顯示alert的方法:

  • 將HTML寫為字符串
  • 通過dangerouslySetInnerHTML設置HTML
  • 在React的componentDidMound()函數中使用純Javascript為它添加onclick函數。
  • 成功

但是, 我們要做的是

  • 創建一些方法,例如: addTwoNumbers或類似的東西
  • 從調用addTwoNumbers函數的HTML字符串中發送onclickonClick
  • 每次我們需要添加一個需要使用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;

考慮到這一點,您是否知道其他任何方式來做我想做的事情?

解決方案有點hacky,在codeandbox中復制粘貼

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上的所有內容。 然后,將您的Web服務器配置為永遠不會緩存較小的包(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的內部工作只有一點知識,並且根本沒有對此進行過測試。

最簡單的方法是使用Dangerously Set innerHTML

function createMarkup() {
  return {__html: 'First &middot; Second'};
}

function MyComponent() {
  return <div dangerouslySetInnerHTML={createMarkup()} />;

但這需要付出代價,將HTML字符串嵌入到模板中會動態導致安全違規,這就是為什么它具有奇怪的語法__html和一個可怕的名稱dangerouslySetInnerHTML。 如果您正在使用此功能,請確保您清理輸入參數以防止任何XSS吸引。 或者,您可以使用iframe

從下面的筆中檢查完整的實現(推薦) https://codepen.io/varmakarthik12/pen/zaOzPw

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM