Extract raw HTML in react-native-render-html custom renderer

6k views Asked by At

I'm using react-native-render-html to render html. The renderers method allows me to provide custom function to render a particular tag. However I want to replace the children with my custom component, using the raw inner HTML from the source.

Consider. I have the below html snippet provided to <HTML /> component:

<a> <b> <c meta="xyz"> Some text </c> <b> </a>

and I have a custom renderer which returns a component that takes a html string and does some magic with it:

const renderers = {
  c: () => (
    <Custom html={/** how do I get "<c meta="xyz"> Some text </c>"? */} />
  )
}
1

There are 1 answers

1
Jules Sam. Randolph On BEST ANSWER

The API was not initially designed to handle these kind of use cases, but as for version 5.0.0, it is very easy!

Version 6.x

import * as React from 'react';
import HTML, {domNodeToHTMLString} from 'react-native-render-html';

function CustomRenderer({ tnode, style, key }) {
  const html = React.useMemo(() => domNodeToHTMLString(tnode.domNode), [tnode]);
  return <Custom key={key} html={html} style={style} />;
}

Version 5.x

Since version 5, it is extremely easy with the help of the new domNodeToHTMLString util, see snippet below:

import * as React from 'react';
import HTML, {domNodeToHTMLString} from 'react-native-render-html';

function customRenderer(htmlAttribs, children, inlineStyle, passProps) {
  const html = domNodeToHTMLString(passProps.domNode);
  return <Custom key={passProp.key} html={html} />;
}

Version 4.x and below

To use this hack, you'll need to add “stringify-entities” to your list of dependencies. What this hack does basically, is to use the alterNode hook to add a very unconventional __rawHtml attribute to the DOM node. The attribute will be thereafter passed to the renderer function.

import * as React from 'react';
import HTML from 'react-native-render-html';
import strigifyEntities from 'stringify-entities';
import Custom from './Custom';

function renderOpeningTag(tag, attributes) {
  const strAttributes = [];
  Object.keys(attributes).forEach((key) => {
    strAttributes.push(`${key}="${strigifyEntities(`${attributes[key]}`)}"`);
  });
  return `<${tag}${strAttributes.length ? ' ' : ''}${strAttributes.join(' ')}>`;
}

function nodeToRawHTML(root) {
  let html = '';
  if (root.type === 'tag') {
    const strChildren = root.children.reduce((prev, curr) => {
      return `${prev}${nodeToRawHTML(curr)}`;
    }, '');
    html = `${renderOpeningTag(root.name, root.attribs)}${strChildren}</${
      root.name
    }>`;
  } else if (root.type === 'text') {
    const text = strigifyEntities(root.data);
    html = text;
  }
  return html;
}

function alterNode(node) {
  if (node.type === 'tag' && node.name === 'c') {
    node.attribs.__rawHtml = nodeToRawHTML(node);
  }
}

const renderers = {
  c: ({__rawHtml}, children, convertedCSSStyles, passProp) => {
    return <Custom key={passProp.key} html={__rawHtml} />
  },
};

export default function App() {
  return (
    <HTML
      renderers={renderers}
      alterNode={alterNode}
      html={'<a> <b> <c meta="xyz"> Some text </c> <b> </a>'}
    />
  );
}