简体   繁体   English

如何将JSX组件与dangerouslySetInnerHTML结合使用

[英]How to combine JSX component with dangerouslySetInnerHTML

I'm displaying text that was stored in the database. 我正在显示存储在数据库中的文本。 The data is coming from firebase as a string (with newline breaks included). 数据来自firebase作为字符串(包括换行符)。 To make it display as HTML, I originally did the following: 为了使它显示为HTML,我最初做了以下事情:

<p className="term-definition"
        dangerouslySetInnerHTML={{__html: (definition.definition) ? definition.definition.replace(/(?:\r\n|\r|\n)/g, '<br />') : ''}}></p>

This worked great. 这很有效。 However there's one additional feature. 但是还有一个额外的功能。 Users can type [word] and that word will become linked. 用户可以输入[word]并且该单词将被链接。 In order to accomplish this, I created the following function: 为了实现这一点,我创建了以下功能:

  parseDefinitionText(text){
    text = text.replace(/(?:\r\n|\r|\n)/g, '<br />');
    text = text.replace(/\[([A-Za-z0-9'\-\s]+)\]/, function(match, word){
      // Convert it to a permalink
      return (<Link to={'/terms/' + this.permalink(word) + '/1'}>{word}</Link>);
    }.bind(this));
    return text;
  },

I left out the this.permalink method as it's not relevant. 我遗漏了this.permalink方法,因为它不相关。 As you can see, I'm attempting to return a <Link> component that was imported from react-router.However since it's raw HTML, dangerouslySetInnerHTML no longer works properly. 正如您所看到的,我正在尝试返回从react-router导入的<Link>组件。但是,因为它是原始HTML,所以dangerouslySetInnerHTML不再正常工作。

So I'm kind of stuck at this point. 所以我有点卡在这一点上。 What can I do to both format the inner text and also create a link? 我可以做什么来格式化内部文本并创建链接?

You could split the text into an array of Links + strings like so: 您可以将文本拆分为Links +字符串数组,如下所示:

import {Link} from 'react-router';

const paragraphWithLinks = ({markdown}) => {
  const linkRegex = /\[([\w\s-']+)\]/g;

  const children = _.chain(
    markdown.split(linkRegex) // get the text between links
  ).zip(
    markdown.match(linkRegex).map( // get the links
      word => <Link to={`/terms/${permalink(word)}/1`}>{word}</Link> // and convert them
    )
  ).flatten().thru( // merge them
    v => v.slice(0, -1) // remove the last element (undefined b/c arrays are different sizes)
  ).value();

  return <p className='term-definition'>{children}</p>;
};

The best thing about this approach is removing the need to use dangerouslySetInnerHTML . 这种方法的最佳之处在于无需使用dangerouslySetInnerHTML Using it is generally an extremely bad idea as you're potentially creating an XSS vulnerability. 使用它通常是一个非常糟糕的主意,因为您可能会创建一个XSS漏洞。 That may enable hackers to, for example, steal login credentials from your users. 例如,这可能使黑客窃取用户的登录凭据。

In most cases you do not need to use dangerouslySetHTML . 在大多数情况下,您不需要使用dangerouslySetHTML The obvious exception is for integration w/ a 3rd party library, which should still be considered carefully. 明显的例外是与第三方库集成,仍应仔细考虑。

I ran into a similar situation, however the accepted solution wasn't a viable option for me. 我遇到了类似的情况,但是接受的解决方案对我来说不是一个可行的选择。

I got this working with react-dom in a fairly crude way. 我以相当粗暴的方式使用react-dom I set the component up to listen for click events and if the click had the class of react-router-link . 我将组件设置为侦听单击事件,如果单击具有react-router-link When this happened, if the item has a data-url property set it uses browserHistory.push . 发生这种情况时,如果该项具有data-url属性设置,则使用browserHistory.push I'm currently using an isomorphic app, and these click events don't make sense for the server generation, so I only set these events conditionally. 我目前正在使用同构应用程序,这些点击事件对于服务器生成没有意义,所以我只是有条件地设置这些事件。

Here's the code I used: 这是我使用的代码:

import React from 'react';
import _ from 'lodash';
import { browserHistory } from 'react-router'

export default class PostBody extends React.Component {
  componentDidMount() {
    if(! global.__SERVER__) {
      this.listener = this.handleClick.bind(this);
      window.addEventListener('click', this.listener);
    }
  }

  componentDidUnmount() {
    if(! global.__SERVER__) {
      window.removeEventListener("scroll", this.listener);
    }
  }

  handleClick(e) {
    if(_.includes(e.target.classList, "react-router-link")) {
      window.removeEventListener("click", this.listener);
      browserHistory.push(e.target.getAttribute("data-url"));
    }
  }

  render() {
    function createMarkup(html) { return {__html: html}; };

    return (
      <div className="col-xs-10 col-xs-offset-1 col-md-6 col-md-offset-3 col-lg-8 col-lg-offset-2 post-body">
        <div dangerouslySetInnerHTML={createMarkup(this.props.postBody)} />
      </div>
    );
  }

}

Hope this helps out! 希望这会有所帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM