Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to put React component inside HTML string?

I have: an array of HTML strings, eg ["<h1>Hi", "</h1>"].
I want to place <MyReactComponent/> in between them
(thus achieving a layout that would be in jsx:
<h1>Hi<MyReactComponent/></h1>).

How do I achieve this?


I've tried:

  • Babel.transform('<h1>Hi<MyReactComponent/></h1>') (using standalone Babel). It does work, but requires me to stringify <MyReactComponent/>, which is not elegant and probably will break some day.

  • to use regular jsx render() => <MyReactComponent/>, and then, on componentDidMount prepending HTML by manipulating DOM, but browser inserts closing tags automatically, so I'll be getting <h1>Hi</h1><MyReactComponent/><h1></h1>

  • to use jsx-to-html library and innerHTML, to convert <MyReactComponent/> to HTML string, combine it with <h1>Hi</h1>, but it destroy any React interaction with <MyReactComponent/>.

like image 385
lakesare Avatar asked Aug 16 '16 00:08

lakesare


1 Answers

You might want to take a look at html-to-react.

This library converts the string to a node tree of DOM elements, then transforms each node to a React element using a set of instructions that you define. I believe that it depends on the string being valid markup though, so you might have to change "<h1>Hi<MyReactComponent/></h1" to something like "<h1>Hi<x-my-react-component></x-my-react-component></h1>.

Example:

import { Parser, ProcessNodeDefinitions } from "html-to-react";
import MyReactComponent from "./MyReactComponent";

const customElements = {
    "x-my-react-component": MyReactComponent
};

// Boilerplate stuff
const htmlParser = new Parser(React);
const processNodeDefinitions = new ProcessNodeDefinitions(React);
function isValidNode(){
    return true;
}

// Custom instructions for processing nodes
const processingInstructions = [
    // Create instruction for custom elements
    {
        shouldProcessNode: (node) => {
            // Process the node if it matches a custom element
            return (node.name && customElements[node.name]);
        },
        processNode: (node) => {
            let CustomElement = customElements[node.name];
            return <CustomElement/>;
        }
    },
    // Default processing
    {
        shouldProcessNode: () => true,
        processNode: processNodeDefinitions.processDefaultNode
    }
];

export default class MyParentComponent extends Component {
    render () {
        let htmlString = "<h1>Hi<x-my-react-component></x-my-react-component></h1>";
        return htmlParser.parseWithInstructions(htmlString, isValidNode, processingInstructions);
    }
}

The essential part here is processingInstructions. Every node in the DOM tree is checked against each instruction in the array, starting from the top, until shouldProcessNode returns true, and the node is transformed to a React element by the corresponding processNode function. This allows for rather complex processing rules, but it quickly gets a bit messy if you want to process nested custom elements. The result of the example is the equivalent of

<h1>
    Hi
    <MyReactComponent/>
</h1>

in JSX syntax. Hope this helps!

like image 191
dannyjolie Avatar answered Sep 22 '22 18:09

dannyjolie