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 best solutions below

1
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>'}
    />
  );
}