1

What I want to do is to fetch a website, then convert it into HTML, but not put the entire HTML into the React DOM, first select an element (and obviously it's children), then put this specific element into the React DOM. What I want is something like this:

convertHtml = () => {
  fetch(url)
  .then(res => res.text())
  .then(htmlString => {
    /*for example the htmlString looks like this:
      "<html>
        <body>
          <div>
            <p id="elementWhatIWant">
              I want to earn this element (not the text inside)
            </p>
          </div>
        </body>
      </html>"
    */
    //what I want:
    return stringToHtml(htmlString).getElementById("elementWhatIWant")
  })
}
render = () => <div className="App">
  {this.convertHtml()}
</div>

I did some research, but as I saw, there's only one way to get an element in React, and it's the ref. Because it's a string, we can't put ref on any element directly. The thing what I thought is that we can parse the string, and

  • search the elementWhatIWant element, and put the ref on it as a string, or
  • search the elementWhatIWant element, search it's closing tag, and return just this part of the string

Neither of them seems like a good approach, that's why I'm asking: What is the best way to earn what I want?

Gergő Horváth
  • 3,195
  • 4
  • 28
  • 64
  • 1
    You probably want [this](https://stackoverflow.com/questions/23616226/insert-html-with-react-variable-statements-jsx), but please be warned about the security implications. Don't take `dangerouslySetInnerHTML` for granted. – Blue Oct 27 '18 at 12:40
  • 1
    Well you also have ReactDom.findDOMNode, so if you combine FrankerZ suggestion with that .. and extract yuor code to a separate component you can do it without much hassle. – Goran.it Oct 27 '18 at 12:45
  • Yes, I read about it, but I want to select just a child from the string, and insert just that to render. – Gergő Horváth Oct 27 '18 at 12:45
  • In that case, create an empty DIV element in memory. Populate it with innerHTML string, and you can get childNodes with querySelector. – Goran.it Oct 27 '18 at 12:46
  • Can you provide me an example of the code of using those methods? – Gergő Horváth Oct 27 '18 at 12:49
  • Here is an example (similar issue): https://stackoverflow.com/questions/49869953/render-or-use-documentfragment-in-a-react-component – Goran.it Oct 27 '18 at 12:53

1 Answers1

3

As previously mentioned, if these are simple HTML elements, you should consider using react-html-parser. It will solve your issue, in a much safer way, as opposed to dangerouslySetInnerHTML below. See my codesandbox here for more info:

class App extends React.Component {
  constructor() {
    super();
    this.state = {
      html: null
    };

    this.fetchHtml = this.fetchHtml.bind(this);
  }

  async fetchHtml() {
    const html = await ajaxPlaceholder();
    console.log("Got new html!", html);

    const parsed = $(html).find("span");

    // From https://stackoverflow.com/a/5744268/4875631
    this.setState({
      html: parsed[0].outerHTML
    });
  }

  render() {
    return (
      <div className="App">
        <h1>Hello CodeSandbox</h1>
        Escaped: <div>{this.state.html}</div>
        <br />
        Not Dangerous: <div>{parser(this.state.html)}</div>
        <button onClick={this.fetchHtml}>Fetch some Html</button>
      </div>
    );
  }
}

I've created a codesandbox here which demonstrates the dangerouslySetInnerHTML functionality. As mentioned, you want to use dangerouslySetInnerHTML={{ __html: HTML_YOU_WANT_TO_SET }} to set html. Please use it with caution:

dangerouslySetInnerHTML is React’s replacement for using innerHTML in the browser DOM. In general, setting HTML from code is risky because it’s easy to inadvertently expose your users to a cross-site scripting (XSS) attack. So, you can set HTML directly from React, but you have to type out dangerouslySetInnerHTML and pass an object with a __html key, to remind yourself that it’s dangerous.

const ajaxPlaceholder = () => {
  return new Promise(resolve => {
    setTimeout(() => resolve("<h1>Test Return</h1>"), 500);
  });
};

class App extends React.Component {
  constructor() {
    super();
    this.state = {
      html: null
    };

    this.fetchHtml = this.fetchHtml.bind(this);
  }

  async fetchHtml() {
    const html = await ajaxPlaceholder();
    console.log("Got new html!", html);

    this.setState({
      html
    });
  }

  render() {
    return (
      <div className="App">
        <h1>Hello CodeSandbox</h1>
        Escaped: <div>{this.state.html}</div>
        <br />
        Dangerous: <div dangerouslySetInnerHTML={{ __html: this.state.html }} />
        <button onClick={this.fetchHtml}>Fetch some Html</button>
      </div>
    );
  }
}
Blue
  • 22,608
  • 7
  • 62
  • 92
  • What if the h1 have a wrapper div as the response of the fetch, but we want to select just the h1? – Gergő Horváth Oct 27 '18 at 12:54
  • @GergőHorváth Consider checking out my updated answer (I provided an example using react-html-parser which is much safer) – Blue Oct 27 '18 at 13:10