Inject JSX-formatted string into React Component

2019-01-20 03:57发布

问题:

I have a small react page that should compile and display html string. the html in the string written in react-foundation

The page looks like this :

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Link, Button, Colors } from 'react-foundation';
require('./foundation.css')

var htmlFromApi = '<div className="button-basics-example"><Button color={Colors.SUCCESS}>Save</Button><Button color={Colors.ALERT}>Delete</Button></div>';

var App = ({ html }) => { return <div>{html}</div> }

ReactDOM.render(
<App html={htmlFromApi}/>,
document.getElementById('react')
);

The Result of the above code is just a string, I am wondering if there is a way to convert the html string to react element

something like this :

var reactElement= toReactElement(htmlFromApi);
//the result is <div className="button-basics-example"><Button color={Colors.SUCCESS}>Save</Button><Button color={Colors.ALERT}>Delete</Button></div>

PS I tried <div className="content" dangerouslySetInnerHTML={{ __html: htmlFromApi }}></div> and didn't work

Thanks in advance

Edit: the code is here

回答1:

I originally provided an answer as to how you can insert a string as HTML directly into React. However, since you also want variables inside your string to be parsed (and other potential logic) before it being inserted into React, I have left both options here as they might be useful for future readers.


Case 1 - Your string is ready to be inserted

If the string containing your HTML is ready to be directly inserted into React, and does not contain code that needs to be parsed first, you should use dangerouslySetInnerHTML. You should set it on the wrapping div that will contain the HTML fetched from your API.

See the example below.

var htmlFromApi = '<div className="button-basics-example"><Button color={Colors.SUCCESS}>Save</Button><Button color={Colors.ALERT}>Delete</Button></div>';

var App = ({html}) => { return <div dangerouslySetInnerHTML={{ __html: html}}></div> }

ReactDOM.render(<App html={htmlFromApi}/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<div id="app"></div>


Case 2 - Your string is not ready and needs additional logic before being inserted

If your string isn't "ready" to be injected directly into the DOM, but needs some kind of processing first (e.g your string contains variables that need to be interpreted first), things get more complicated. Unfortunately, there is no good nor "recommended" way to do this.

  • If the manipulation required is simple and fairly static, you could perhaps use regex. Though this approach comes with limitations. Use cases for this approach might be manipulating a DOM class or adding an element to the string.
  • Another approach is to use a library, such as html-to-react, that might help you with what you are looking for.
  • Finally, if you are using Babel (which you almost certainly are), you could use the Babel transformer. All you need is to import babel-core into your code and transform the string into JSX. This approach might be more limited than using a library, but it should suffice.

Here's how to implement point #3 from above:

Babel.transform(code, options);

Where code is your html-string. In options we need to pass an object with {presets: ['react']} so that Babel understands that want JSX as our output. You could of course add other options here also.

This will return an object that contains stuff like the source-map, but we are only interested in the generated code here. So we need to add:

Babel.transform(code, options).code;

Note that code is javascript code in a string-format and it expresses a function call to create a React Element with React.createElement. To execute a string as code in javascript, we can use eval().

We can then add this React Element into our React Component and render it normally, as shown in the example below.


var htmlFromApi = '<div className="button-basics-example"><Button color={Colors.SUCCESS}>Save</Button><Button color={Colors.ALERT}>Delete</Button></div>';
var Colors = {SUCCESS: "green", ALERT: "red"};

var App = ({html}) => {
  var Component = Babel.transform(htmlFromApi, {presets: ["react"]}).code;
  return <div>{eval(Component)}</div>;
};

var Button = ({color, children}) => (
  <button style={{backgroundColor: color}}>{children}</button>
);

ReactDOM.render(<App />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

For more details about Babel.transform, see the official documentation.