Avi
Avi

Reputation: 267

Parse and Render external HTML in React component

I'm writing a React-based application where one of the components receives its HTML content as a string field in props. This content is returned by an API call.

I need to:

  1. Render this content as a standard HTML (i.e. with the styles applied)
  2. Parse the content to see if the sections within the content have "accept-comments" tag and show a "Comment" button beside the section

For example, if I receive the HTML below, I should show the "Comment" button beside section with id "s101".

<html>
    <head/>
    <body>
        <div id="content">
            <section id="s101" accept-comments="true">Some text that needs comments</section>
            <section id="s102">Some text that doesn't need comments</section>
        </div>
    </body>
</html>

Questions:

  1. What would be the most efficient way to parse and render the HTML as the content can get a bit large, close to 1MB at times?
  2. How can I ensure that React does not re-render this component as it will not be updated? I'd assume always return "false" from shouldComponentUpdate().

Things I've tried:

  1. Render the HTML with "dangerouslySetInnerHTML" or "react-html-parser". With this option, cannot parse the "accept-comments" sections.
  2. Use DOMParser().parseFromString to parse the content. How do I render its output in a React component as HTML? Will this be efficient with 1MB+ content?

Upvotes: 8

Views: 14815

Answers (1)

Avi
Avi

Reputation: 267

This answer comes from Chris G's code in the comments. I used the code with different sizes of documents and it works well. Thanks Chris G!

Posting the code here in case the link link in the comments breaks.

The solution uses DOMParser to parse the HTML content provided by the API call and scans it to find the content that should include the "Comment" button. Here are the relevant parts.

import React from "react";
import { render } from "react-dom";

const HTML =
  "<div><section but='yes'>Section 1</section><section>Section 2</section></div>";

class DOMTest extends React.Component {
  constructor(props) {
    super(props);

    const doc = new DOMParser().parseFromString(HTML, "application/xml");
    const htmlSections = doc.childNodes[0].childNodes;

    this.sections = Object.keys(htmlSections).map((key, i) => {
      let el = htmlSections[key];
      let contents = [<p>{el.innerHTML}</p>];

      if (el.hasAttribute("but")) contents.push(<button>Comment</button>);

      return <div key={i}>{contents}</div>;
    });
  }

  render() {
    return <div>{this.sections}</div>;
  }
}

const App = () => (
  <div>
    <DOMTest />
  </div>
);

render(<App />, document.getElementById("root"));

Upvotes: 11

Related Questions