ChatGPT解决这个技术问题 Extra ChatGPT

Render HTML string as real HTML in a React component

Here's what I tried and how it goes wrong.

This works:

<div dangerouslySetInnerHTML={{ __html: "<h1>Hi there!</h1>" }} />

This doesn't:

<div dangerouslySetInnerHTML={{ __html: this.props.match.description }} />

The description property is just a normal string of HTML content. However it's rendered as a string, not as HTML for some reason.

https://i.stack.imgur.com/cI25q.png

Any suggestions?


D
Dexter

Is this.props.match.description a string or an object? If it's a string, it should be converted to HTML just fine. Example:

class App extends React.Component {

constructor() {
    super();
    this.state = {
      description: '<h1 style="color:red;">something</h1>'
    }
  }
  
  render() {
    return (
      <div dangerouslySetInnerHTML={{ __html: this.state.description }} />
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));

Result: http://codepen.io/ilanus/pen/QKgoLA?editors=1011

However if description is <h1 style="color:red;">something</h1> without the quotes '', you're going to get:

​Object {
$$typeof: [object Symbol] {},
  _owner: null,
  key: null,
  props: Object {
    children: "something",
    style: "color:red;"
  },
  ref: null,
  type: "h1"
}

If It's a string and you don't see any HTML markup the only problem I see is wrong markup..

UPDATE

If you are dealing with HTML Entities, You need to decode them before sending them to dangerouslySetInnerHTML that's why it's called "dangerously" :)

Working example:

class App extends React.Component {

  constructor() {
    super();
    this.state = {
      description: '&lt;p&gt;&lt;strong&gt;Our Opportunity:&lt;/strong&gt;&lt;/p&gt;'
    }
  }

   htmlDecode(input){
    var e = document.createElement('div');
    e.innerHTML = input;
    return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
  }

  render() {
    return (
      <div dangerouslySetInnerHTML={{ __html: this.htmlDecode(this.state.description) }} />
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));

this.props.match.description is a string, not an object. What do you mean with wrong markup? Do you mean unclosed tags? React should just render it no?
Could you paste here console.log(this.props.match.description);
One example: &lt;p&gt;&lt;strong&gt;Our Opportunity:&lt;/strong&gt;&lt;/p&gt;
In this case you need either to use .innerHTML or decode HTMLEntities.
Return multiple lines or HTML code with tags: function htmlDecode(input){ var e = document.createElement('div'); e.innerHTML = input; var returnString = ''; for (index = 0; index < e.childNodes.length; index++) { // case of just a string if(e.childNodes[index].nodeValue){ returnString += e.childNodes[index].nodeValue; } // case of HTML if(e.childNodes[index].outerHTML){ returnString += e.childNodes[index].outerHTML; } } return returnString; }
p
pixelearth

I use 'react-html-parser'

yarn add react-html-parser
import ReactHtmlParser from 'react-html-parser'; 

<div> { ReactHtmlParser (html_string) } </div>

Source on npmjs.com

Lifting up @okram's comment for more visibility:

from its github description: Converts HTML strings directly into React components avoiding the need to use dangerouslySetInnerHTML from npmjs.com A utility for converting HTML strings into React components. Avoids the use of dangerouslySetInnerHTML and converts standard HTML elements, attributes and inline styles into their React equivalents.


Does this library use "dangerouslySetInnerHTML" in the background?
from its github description: Converts HTML strings directly into React components avoiding the need to use dangerouslySetInnerHTML from npmjs.com A utility for converting HTML strings into React components. Avoids the use of dangerouslySetInnerHTML and converts standard HTML elements, attributes and inline styles into their React equivalents.
For React 17.0+, I use a similar library called "html-react-parser", which is currently supported. npmjs.com/package/html-react-parser
From FAQ section of html-react-parser: "this library is not XSS (cross-site scripting) safe." npmjs.com/package/html-react-parser
You may need to install with : npm install react-html-parser
m
machineghost

Check if the text you're trying to append to the node is not escaped like this:

var prop = {
    match: {
        description: '&lt;h1&gt;Hi there!&lt;/h1&gt;'
    }
};

Instead of this:

var prop = {
    match: {
        description: '<h1>Hi there!</h1>'
    }
};

if is escaped you should convert it from your server-side.

https://i.stack.imgur.com/LF1X0.png

The node is text because is escaped

https://i.stack.imgur.com/tAKRd.png

The node is a dom node because isn't escaped


This was the issue. The description string was escaped HTML. I unescaped it and now it works fine.
Please avoid using dangerouslySetInnerHTML instead use Fragment from react v16. Check the next answer by @brad-adams
Appreciate the mention @KunalParekh, but they're different things. My answer is only valid if the html is located within your app (meaning it's actually JSX). To parse HTML from an external source to jsx you'd need to seek another solution.
D
DᴀʀᴛʜVᴀᴅᴇʀ

If you have HTML in a string I would recommend using a package called html-react-parser.

Installation

NPM:

npm install html-react-parser

yarn:

yarn add html-react-parser

Usage

import parse from 'html-react-parser'
const yourHtmlString = '<h1>Hello</h1>'

code:

<div>
    {parse(yourHtmlString)}
</div>

B
Brad Adams

If you have control over where the string containing html is coming from (ie. somewhere in your app), you can benefit from the new <Fragment> API, doing something like:

import React, {Fragment} from 'react'

const stringsSomeWithHtml = {
  testOne: (
    <Fragment>
      Some text <strong>wrapped with strong</strong>
    </Fragment>
  ),
  testTwo: `This is just a plain string, but it'll print fine too`,
}

...

render() {
  return <div>{stringsSomeWithHtml[prop.key]}</div>
}

There is no string containing html in your example. It's either jsx or plain string.
Well, yeah technically you're correct @mrkvon, however as I mention, this solution is only valid if said "html"/jsx is something you have control over. Not for rendering some raw html provided via an API, for example. Prior to the Fragment API it was always a pain for me, that required additional span wraps that would sometimes mess with flex layouts. When I stumbled upon this question looking for a possible solution I thought I'd share how I got around things.
Thanks! This was the only solution that worked in my case. Also, responding to mrkvon's comment on this answer : This answer indeed contains html i.e Some text <strong>wrapped with strong</strong> contains html tag strong.
@BinitaBharati But that's not a string though. If you get a string from an API like "

This is a String

"(or simply store a string in a variable), when you put this string in , the output will still contain the

tag.

@BradAdams. Nice trick though. I can see the instances where it becomes handy.
L
Lorenzo Delana

I use innerHTML together a ref to span:

import React, { useRef, useEffect, useState } from 'react';

export default function Sample() {
  const spanRef = useRef<HTMLSpanElement>(null);
  const [someHTML,] = useState("some <b>bold</b>");

  useEffect(() => {
    if (spanRef.current) {
      spanRef.current.innerHTML = someHTML;
    }
  }, [spanRef.current, someHTML]);

  return <div>
    my custom text follows<br />
    <span ref={spanRef} />
  </div>
}

UPDATE:

I removed someHTML state and added comments to make the example more coincise around the concept.

/**
 * example how to retrieve a reference to an html object
 */

import React, { useRef, useEffect } from 'react';

/**
 * this component can be used into another for example <Sample/>
 */
export default function Sample() {
    /**
     * 1) spanRef is now a React.RefObject<HTMLSpanElement>
     * initially created with null value
     */
    const spanRef = useRef<HTMLSpanElement>(null);

    /**
     * 2) later, when spanRef changes because html span element with ref attribute,
     * follow useEffect hook will triggered because of dependent [spanRef].
     * in an if ( spanRef.current ) that states if spanRef assigned to valid html obj
     * we do what we need : in this case through current.innerHTML
     */
    useEffect(() => {
        if (spanRef.current) {
            spanRef.current.innerHTML = "some <b>bold</b>";
        }
    }, [spanRef]);

    return <div>
        my custom text follows<br />
        {/* ref={spanRef] will update the React.RefObject `spanRef` when html obj ready */}
        <span ref={spanRef} />
    </div>
}

I like this, no need for additional libraries or reliance on server-side when you don't have that luxury. Inspired by you, but in a class component I did componentDidMount() { this.message.current.innerHTML = this.state.selectedMessage.body; } body is the escaped html for me.
A little explanation to the answer could have done wonders.
@letsbondiway see UPDATE part on my answer.
@LorenzoDelana Thank you for the detailed updated answer. Now it is really helpful. However, I had a question - do you think there is any kind of security risks associated with this solution? I mean attacks like XSS, HTML injection and all. My understanding is it is safe from these as we are not using dangerouslySetInnerHTML
@letsbondiway with or without setting an html element property in a direct way such the innerHTML can be dangerous if security criteria aren't applied ; from my point of view, but of course I could miss something, there isn't specific issues if you know what you are doing and how these could negatively used from an attackers. For counter example you can regularly use standard input box provided by the framework that of course is good because best-practices are already taken in account, but if you use that text as a part of raw sql query an attacker could inject a tautology to extract all data.
H
Hou Soune

You just use dangerouslySetInnerHTML method of React

<div dangerouslySetInnerHTML={{ __html: htmlString }} />

Or you can implement more with this easy way: Render the HTML raw in React app


S
Salman Lone

In my case, I used react-render-html

First install the package by npm i --save react-render-html

then,

import renderHTML from 'react-render-html';

renderHTML("<a class='github' href='https://github.com'><b>GitHub</b></a>")

Meanwhile, I do not suggest using react-render-html anymore. Its package health score is very low due to snyk: snyk.io/advisor/npm-package/react-render-html Html-react-parser is much better: snyk.io/advisor/npm-package/html-react-parser
B
Binita Bharati

I could not get npm build to work with react-html-parser. However, in my case, I was able to successfully make use of https://reactjs.org/docs/fragments.html. I had a requirement to show few html unicode characters , but they should not be directly embedded in the JSX. Within the JSX, it had to be picked from the Component's state. Component code snippet is given below :

constructor() 
{
this.state = {
      rankMap : {"5" : <Fragment>&#9733; &#9733; &#9733; &#9733; &#9733;</Fragment> , 
                 "4" : <Fragment>&#9733; &#9733; &#9733; &#9733; &#9734;</Fragment>, 
                 "3" : <Fragment>&#9733; &#9733; &#9733; &#9734; &#9734;</Fragment> , 
                 "2" : <Fragment>&#9733; &#9733; &#9734; &#9734; &#9734;</Fragment>, 
                 "1" : <Fragment>&#9733; &#9734; &#9734; &#9734; &#9734;</Fragment>}
                };
}

render() 
{
       return (<div class="card-footer">
                    <small class="text-muted">{ this.state.rankMap["5"] }</small>
               </div>);
}

Y
Yanov

i use https://www.npmjs.com/package/html-to-react

const HtmlToReactParser = require('html-to-react').Parser;
let htmlInput = html.template;
let htmlToReactParser = new HtmlToReactParser();
let reactElement = htmlToReactParser.parse(htmlInput); 
return(<div>{reactElement}</div>)

M
MajiD

You can also use parseReactHTMLComponent from Jumper Package. Just look at it, it's easy and you don't need to use JSX syntax.

https://codesandbox.io/s/jumper-module-react-simple-parser-3b8c9?file=/src/App.js .

More on Jumper:

https://github.com/Grano22/jumper/blob/master/components.js

NPM Package:

https://www.npmjs.com/package/jumper_react


A
Abdul Basit

// For typescript import parse, { HTMLReactParserOptions } from "html-react-parser"; import { Element } from "domhandler/lib/node"; export function contentHandler(postContent: string) { const options: HTMLReactParserOptions = { replace: (domNode: Element) => { if (domNode.attribs) { if (domNode.attribs.id === 'shortcode') { return

Shortcode
; } } }, }; return parse(postContent, options); } // Usage: contentHandler("Hello World!")


B
BBRay

If you have control to the {this.props.match.description} and if you are using JSX. I would recommend not to use "dangerouslySetInnerHTML".

// In JSX, you can define a html object rather than a string to contain raw HTML
let description = <h1>Hi there!</h1>;

// Here is how you print
return (
    {description}
);