Gergő Horváth
Gergő Horváth

Reputation: 3705

How to make HTML elements from string, then select an element in React?

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

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

Upvotes: 1

Views: 2525

Answers (1)

Blue
Blue

Reputation: 22911

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>
    );
  }
}

Upvotes: 3

Related Questions