在React组件中将HTML字符串呈现为真实HTML


159

这是我尝试过的方法以及出现问题的方法。

这有效:

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

这不是:

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

description属性只是HTML内容的普通字符串。但是由于某种原因,它呈现为字符串,而不是HTML。

在此处输入图片说明

有什么建议?

Answers:


50

检查您尝试追加到节点的文本是否没有像这样转义:

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

代替这个:

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

如果转义,则应从服务器端进行转换。

该节点是文本,因为已转义

该节点是文本,因为已转义

该节点是dom节点,因为没有转义

该节点是dom节点,因为没有转义


3
这就是问题所在。说明字符串已转义为HTML。我未进行转义,现在可以正常工作。
塞尔吉奥·塔皮亚

4
请避免使用dangerouslySetInnerHTML,而不是使用Fragment从反应V16。通过@ brad-adams 检查下一个答案
Kunal Parekh

2
感谢@KunalParekh的提及,但他们是不同的。我的答案只有在html位于您的应用程序内时才有效(这意味着它实际上是JSX)。要将HTML从外部源解析为jsx,您需要寻求其他解决方案。
布拉德·亚当斯

113

确实this.props.match.description是一个字符串或一个对象?如果是字符串,则应将其转换为HTML。例:

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'));

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

但是,如果description: <h1 style="color:red;">something</h1>没有引号,''您将得到:

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

如果它是字符串,并且您没有看到任何HTML标记,那么我看到的唯一问题是标记错误。

更新

如果您正在处理HTMLEntitles。您需要先解码它们,然后才能发送给他们,dangerouslySetInnerHTML这就是为什么他们危险地称呼它:)

工作示例:

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是一个字符串,而不是一个对象。错误标记是什么意思?您是说未封闭标签吗?React应该只显示否吗?
塞尔吉奥·塔皮亚

您可以在此处粘贴console.log(this.props.match.description);
Ilanus

一个例子:&lt;p&gt;&lt;strong&gt;Our Opportunity:&lt;/strong&gt;&lt;/p&gt;
Sergio Tapia

在这种情况下,您需要使用.innerHTML或解码HTMLEntities。
伊兰努斯

返回带有标签的多行或HTML代码:function htmlDecode(input){var e = document.createElement('div'); e.innerHTML =输入;var returnString =''; for(index = 0; index <e.childNodes.length; index ++){//仅是字符串的情况if(e.childNodes [index] .nodeValue){returnString + = e.childNodes [index] .nodeValue; } // HTML的情况if(e.childNodes [index] .outerHTML){returnString + = e.childNodes [index] .outerHTML; returnString; }
克里斯·亚当斯

58

我使用'react-html-parser'

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

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

来源上npmjs.com

提升@okram的评论以提高知名度:

从其github描述中:直接将HTML字符串转换为React组件,而无需使用npmjs.com的危险的SetInnerHTML实用程序,可将HTML字符串转换为React组件。避免使用dragonallySetInnerHTML,并将标准HTML元素,属性和内联样式转换为其等效的React。


11
该库是否在后台使用“ dangerouslySetInnerHTML”?
奥马尔

1
来自其github描述:Converts HTML strings directly into React components avoiding the need to use dangerouslySetInnerHTML来自npmjs.comA 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.
okram

14

如果您可以控制包含html的字符串的来源(例如,应用程序中的某个位置),则可以从新<Fragment>API中受益,方法如下:

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>
}

11
您的示例中没有包含html的字符串。它是jsx或纯字符串。
mrkvon '18

3
是的,从技术上来说,您是正确的@mrkvon,但是正如我所提到的,仅当您说“ html” / jsx是您可以控制的东西时,此解决方案才有效。例如,不用于呈现通过API提供的一些原始 html。在使用FragmentAPI 之前,对我来说一直很痛苦,需要额外的span包装,这些包装有时会与flex布局混在一起。当我偶然发现了这个问题,寻找可能的解决办法,我想我会分享如何得到了周围的事物。
布拉德·亚当斯

2
谢谢!这是对我而言唯一有效的解决方案。另外,回应mrkvon对这个答案的评论:这个答案确实包含html即Some text <strong>wrapped with strong</strong>包含html tag strong
Binita Bharati

@BinitaBharati但这不是一个字符串。如果您从“ <p>这是一个字符串</ p>”之类的API获得了一个字符串(或者只是将字符串存储在变量中),则将该字符串放入<Fragment>时,输出仍将包含< p>标签。
Muchdecal

1
@BradAdams。虽然不错。我可以看到实例变得很方便。
Muchdecal


5

危险地设置内部HTML

angerouslySetInnerHTML是React在浏览器DOM中使用innerHTML的替代。通常,从代码设置HTML是有风险的,因为很容易无意间使您的用户遭受跨站点脚本(XSS)攻击。因此,您可以直接从React设置HTML,但是您必须键入hazardlySetInnerHTML并使用__html键传递对象,以提醒自己这很危险。例如:

function createMarkup() {
  return {__html: 'First &middot; Second'};
}

function MyComponent() {
  return <div dangerouslySetInnerHTML={createMarkup()} />;
}

3

我一起使用innerHTML引用来跨越:

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>
}

我喜欢这样,当您没有那么奢侈的时候,不需要额外的库或依赖服务器端。受您的启发,但在类组件中,我做的 componentDidMount() { this.message.current.innerHTML = this.state.selectedMessage.body; }身体对我来说是逃脱的html。
猎犬

2

就我而言,我使用了react-render-html

首先安装软件包 npm i --save react-render-html

然后,

import renderHTML from 'react-render-html';

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

1

我无法npm build与一起工作react-html-parser。但是,就我而言,我能够成功使用https://reactjs.org/docs/fragments.html。我要求显示一些html unicode字符,但它们不应直接嵌入JSX中。在JSX中,必须从组件的状态中选择它。组件代码段如下所示:

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>);
}


-2

如果您可以控制{this.props.match.description}并且使用的是JSX。我建议不要使用“ 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}
);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.