Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to parse a String containing HTML to React Components

I'm using this library: html-to-react as I found that it was possible to "compile" HTML as a String to React Components.

We're doing a system based on widgets and they might change over time (i.e. add / remove / update / etc), so we're planning to send the widget HTML from the database to the front which is done using React.

I've successfully done a simple widget using HTML, now I'm trying this widget to respond to click events, so I thought on adding onclick=method() method from HTML or onClick={method} from React. However I've not been able to get the desired output as I'm getting these errors in my browser's console.

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)

The code I'm using is the following:

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"
  }
}

Is it possible to get an alert or something similar using the above library to parse HTML to React Components? If so, how could I do it? Or what am I doing wrong?


Edit

It doesn't have to be specifically the library I'm using, if there's another one you've worked with and done something similar, I'm open to receive this recommendations (Note, I'm not asking for a software or library, but how to call the onClick method inside the String containing my HTML or a workaround)


Edit 2

After trying something I found that removing this line:

var reactHtml = ReactDOMServer.renderToStaticMarkup(reactElement);

Gets rid of one of the messages. The library uses that element to test if both the current HTML and the parsed one are the same. I didn't needed it for my case but that still leaves the current error in the console:

 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)

Edit 3

After some investigation I found dangerousSetInnerHTML in this answer

Then I tried to follow this answer an placing an alert inside my HTML as follows:

'<label htmlFor="float-input" onclick="alert(\'Hello\')">Hello2</label>'

And it triggered the alert, however if I declared myFunction like in the code below (notice I tried to declare it as a function outside the class, inside it and also as var myFunction = function() { ... } all together and separated):

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;

But this time I get a new error:

ReferenceError: myFunction is not defined[Learn More]

I found a similar question: Handling onClick of a <a> tag inside of dangerouslySetInnerHTML/regular html with this problem, and tried to investigate a little more and found this comment which says that the events from the dangerouslySetInnerHTML won't be triggered this way and links to the docs.

So, while this seemed to be a workaround when I got an alert when written directly from the onclick method, or probably I didn't do it in the correct way, so if someone with more experience could try it and clarify, would be great.


Edit 4

I found a way to show an alert in this way:

  • Write the HTML as a String
  • Set the HTML through dangerouslySetInnerHTML
  • On componentDidMound() function from React use pure Javascript to add onclick function to it.
  • Success

However, what we're trying to do is:

  • Create some methods, for example: addTwoNumbers or something like that
  • Send an onclick or onClick from within the HTML String calling addTwoNumbers function.
  • Repeat everytime we need to add a new component that needs to use the addTwoNumbers method and just have to write the HTML for it, save it on the database and retrieve it and then just use it.

Something like:

...
    <label onClick={myFunction}> My Label </label>
...

Or as I said above:

...
    <label onClick={addTwoNumbers}> My Label </label>
...

The code that allows me to get an alert is the following:

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;

With this in mind, do you know any other way to do what I'm trying to do?

like image 241
Frakcool Avatar asked May 18 '18 23:05

Frakcool


People also ask

How do you render a string with HTML tags in React?

To render the html string in react, we can use the dangerouslySetInnerHTML attribute which is a react version of dom innerHTML property. The term dangerously is used here to notify you that it will be vulnerable to cross-site scripting attacks (XSS).

How convert HTML to text in ReactJS?

There're several ways to convert HTML Strings to JSX with React. One way is to put the string in between curly braces in our component. Another way is to put the string in an array with other strings or JSX code. Finally, we can use the dangerouslySetInnerHTML prop render an HTML string as HTML in our component.

Is React HTML parser safe?

Is react-html-parser safe to use? The npm package react-html-parser was scanned for known vulnerabilities and missing license, and no issues were found. Thus the package was deemed as safe to use.


1 Answers

The solution is a bit hacky , copy paste in 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>
    );
  }
}
like image 162
Rahil Ahmad Avatar answered Oct 12 '22 11:10

Rahil Ahmad