8

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?
4
  • So I assume it's a given that you cannot change what the API returns? Commented Apr 5, 2018 at 5:59
  • That's true. I cannot change what API returns. I need to work with the HTML content it returns. Commented Apr 5, 2018 at 6:06
  • Ok, so you've tried DOMParser(). It returns a document, which means you can turns its childNodes into elements: codesandbox.io/s/x9vp5452z4 Commented Apr 5, 2018 at 6:29
  • Thanks Chris, the code snippet is quite helpful. I will try this with one of the largish documents and update. Commented Apr 5, 2018 at 6:47

1 Answer 1

11

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"));
Sign up to request clarification or add additional context in comments.

2 Comments

one thing to note is the mimetype - may want to specify text/html vs application/xml so that the DOM recognizes predefined HTML elements.
ssr causes issue with DOMParser as it is browser only.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.