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

时间:2015-11-16 03:56:22

标签: javascript reactjs react-router react-router-component

我显示存储在数据库中的文本。数据来自firebase作为字符串(包括换行符)。为了使其显示为HTML,我最初做了以下内容:

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

这很有效。然而,还有一个额外的功能。用户可以键入[word],该字词将被链接。为了实现这一点,我创建了以下函数:

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

我遗漏了this.permalink方法,因为它不相关。正如您所看到的,我尝试返回从react-router导入的<Link>组件。但是,由于它是原始HTML,dangerouslySetInnerHTML不再正常工作。

所以我有点卡在这一点上。我可以做什么来格式化内部文本并创建链接?

2 个答案:

答案 0 :(得分:2)

您可以将文本拆分为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>;
};

这种方法的最佳之处在于无需使用dangerouslySetInnerHTML。使用它通常是非常糟糕的主意,因为您可能会创建XSS漏洞。例如,这可能使黑客窃取用户的登录凭据。

在大多数情况下,您不需要使用dangerouslySetHTML。明显的例外是与第三方库集成,仍应仔细考虑。

答案 1 :(得分:0)

我遇到了类似的情况,但是接受的解决方案对我来说不是一个可行的选择。

我以相当粗暴的方式使用react-dom。我将组件设置为侦听点击事件,如果点击的类别为react-router-link。发生这种情况时,如果项目具有data-url属性集,则使用browserHistory.push。我目前正在使用同构应用,这些点击事件对服务器生成没有意义,所以我只是有条件地设置这些事件。

这是我使用的代码:

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

}

希望这会有所帮助!