【问题标题】:Render HTML string as real HTML in a React component在 React 组件中将 HTML 字符串呈现为真实的 HTML
【发布时间】:2021-08-17 08:41:49
【问题描述】:

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

这行得通:

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

这不是:

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

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

有什么建议吗?

【问题讨论】:

    标签: javascript html reactjs jsx


    【解决方案1】:

    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

    但是,如果描述是 &lt;h1 style="color:red;"&gt;something&lt;/h1&gt; 没有引号 '',你会得到:

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

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

    更新

    如果您正在处理 HTML 实体,您需要在将它们发送到 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);
    • 一个例子:&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Our Opportunity:&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
    • 在这种情况下,您需要使用 .innerHTML 或解码 HTMLEntities。
    • 返回多行或带有标签的 HTML 代码:function htmlDecode(input){ var e = document.createElement('div'); e.innerHTML = 输入; var returnString = ''; for (index = 0; index
    【解决方案2】:

    我使用'react-html-parser'

    yarn add react-html-parser
    
    import ReactHtmlParser from 'react-html-parser'; 
    
    <div> { ReactHtmlParser (html_string) } </div>
    
    

    来源on npmjs.com

    提升@okram 的评论以获得更多可见性:

    来自其 github 描述:将 HTML 字符串直接转换为 React 组件避免使用危险的SetInnerHTML npmjs.com 用于将 HTML 字符串转换为 React 组件的实用程序。 避免使用 dangerouslySetInnerHTML 并转换标准 HTML 元素、属性和内联样式到它们的 React 等效项中。

    【讨论】:

    • 这个库在后台使用“dangerouslySetInnerHTML”吗?
    • 来自其 github 描述: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.
    • 这应该是问题的实际答案。还是谢谢。
    • 对于 React 17.0+,我使用了一个类似的库,叫做“html-react-parser”,目前支持。 npmjs.com/package/html-react-parser
    • 来自 html-react-parser 的 FAQ 部分:“这个库不是 XSS(跨站点脚本)安全的。” npmjs.com/package/html-react-parser
    【解决方案3】:

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

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

    而不是这个:

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

    如果被转义,你应该从你的服务器端转换它。

    节点是文本,因为被转义了

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

    【讨论】:

    • 这就是问题所在。描述字符串是转义的 HTML。我取消了它,现在它工作正常。
    • 请避免使用 dangerouslySetInnerHTML 而不是使用 react v16 中的 Fragment。检查@brad-adams 的next answer
    • 感谢@KunalParekh 的提及,但它们是不同的东西。我的回答只有在 html 位于您的应用程序中时才有效(这意味着它实际上是 JSX)。要将 HTML 从外部源解析为 jsx,您需要寻求另一种解决方案。
    【解决方案4】:

    如果字符串中有 HTML,我建议使用名为 html-react-parser 的包。

    安装它:npm install html-react-parser 或者如果你使用纱线,yarn add html-react-parser

    像这样使用它

    import parse from 'html-react-parser'
    const yourHtmlString = '<h1>Hello</h1>'
    
    <div>
        {parse(yourHtmlString)}
    </div>
    

    参考:https://www.npmjs.com/package/html-react-parser

    【讨论】:

    • 效果很好,不会在原始 HTML 中添加任何额外的 div
    • 这确实很完美!
    • 这是一个很棒的发现
    【解决方案5】:

    如果您可以控制包含 html 的字符串的来源(即您的应用程序中的某处),您可以从新的&lt;Fragment&gt; 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>
    }
    

    【讨论】:

    • 您的示例中没有包含 html 的字符串。它可以是 jsx 或纯字符串。
    • 嗯,是的,从技术上讲,你是正确的@mrkvon,但是正如我所提到的,这个解决方案只有在你可以控制的情况下才有效。例如,不适用于渲染通过 API 提供的一些 raw html。在Fragment API 之前,这对我来说总是很痛苦,因为它需要额外的span 包装,这有时会与弹性布局混淆。当我偶然发现这个问题寻找可能的解决方案时,我想我会分享 如何解决问题。
    • 谢谢!这是在我的情况下唯一有效的解决方案。另外,回复mrkvon对此答案的评论:这个答案确实包含html,即Some text &lt;strong&gt;wrapped with strong&lt;/strong&gt;包含html标签strong
    • @BinitaBharati 但这不是一个字符串。如果你从 API 中得到一个字符串,比如“

      This is a String

      ”(或者只是将一个字符串存储在一个变量中),当你把这个字符串放在 中时,输出仍然会包含 标签。
    • @BradAdams。不错的把戏。我可以看到它变得方便的实例。
    【解决方案6】:

    你只是使用 React 的危险SetInnerHTML 方法

    &lt;div dangerouslySetInnerHTML={{ __html: htmlString }} /&gt;

    或者你可以用这个简单的方法实现更多:Render the HTML raw in React app

    【讨论】:

      【解决方案7】:

      我将innerHTML 与一个ref 一起使用来跨越:

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

      更新

      我删除了一些 HTML 状态并添加了 cmets 以使示例在概念上更加简洁。

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

      【讨论】:

      • 我喜欢这个,当你没有那么奢侈时,不需要额外的库或依赖服务器端。受到您的启发,但在一个类组件中我做了componentDidMount() { this.message.current.innerHTML = this.state.selectedMessage.body; } body 是我的转义 html。
      • 对答案的一点解释可能会产生奇迹。
      • @letsbondiway 请参阅我的回答中的更新部分。
      • @LorenzoDelana 感谢您提供详细的更新答案。现在真的很有帮助。但是,我有一个问题 - 您认为此解决方案是否存在任何类型的安全风险?我的意思是像 XSS、HTML 注入等攻击。我的理解是这些是安全的,因为我们没有使用危险的SetInnerHTML
      • @letsbondiway 以直接方式设置或不设置 html 元素属性,如果不应用安全标准,innerHTML 可能会很危险;从我的角度来看,但当然我可能会错过一些东西,如果您知道自己在做什么以及攻击者如何负面使用这些问题,那么就没有具体问题。作为反例,您可以定期使用框架提供的标准输入框,这当然很好,因为已经考虑了最佳实践,但是如果您将该文本用作原始 sql 查询的一部分,攻击者可能会注入重言式来提取所有数据。
      【解决方案8】:

      就我而言,我使用了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>")
      

      【讨论】:

        【解决方案9】:

        我无法让npm buildreact-html-parser 一起工作。但是,就我而言,我能够成功使用https://reactjs.org/docs/fragments.html。我要求显示几个 html unicode 字符,但它们不应该直接嵌入到 JSX 中。在 JSX 中,它必须从组件的状态中选取。组件代码sn-p如下:

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

        【讨论】:

          【解决方案10】:

          我使用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>)
          

          【讨论】:

            【解决方案11】:

            您也可以使用 Jumper Package 中的 parseReactHTMLComponent。看看就好,很简单,不用JSX语法。

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

            更多关于跳线:

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

            NPM 包:

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

            【讨论】:

              【解决方案12】:

                // 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 <div className="leadform">Shortcode</div>;
                      }
                    }
                  },
                };
              
                return parse(postContent, options);
              }
              
              // Usage: contentHandler("<span>Hello World!</span>")

              【讨论】:

                【解决方案13】:

                如果您可以控制 {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}
                );
                

                【讨论】:

                  猜你喜欢
                  • 2022-01-25
                  • 2022-12-01
                  • 2020-06-11
                  • 2022-08-24
                  • 2016-07-28
                  • 2015-02-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多