ReactコンポーネントでHTML文字列を実際のHTMLとしてレンダリングする


159

これが私が試したこととそれがどうやってうまくいかないかです。

これは機能します:

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

これはしません:

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

descriptionプロパティは、HTMLコンテンツの通常の文字列です。ただし、何らかの理由でHTMLではなく文字列としてレンダリングされます。

ここに画像の説明を入力してください

助言がありますか?

回答:


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でした。エスケープを解除したところ、正常に動作しました。
セルジオタピア2016

4
dangerouslySetInnerHTML代わりにFragmentreact v16からの使用は避けてください。@ brad-adams の次の回答をチェック
Kunal Parekh

2
@KunalParekhへの言及に感謝しますが、それらは別物です。私の回答は、htmlがアプリ内にある場合にのみ有効です(つまり、実際にはJSXです)。HTMLを外部ソースからjsxに解析するには、別のソリューションを探す必要があります。
Brad Adams

113

DOESは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はそれをノーにレンダリングするべきですか?
セルジオタピア2016

ここに貼り付けてくださいconsole.log(this.props.match.description);
Ilanus

1つの例:&lt;p&gt;&lt;strong&gt;Our Opportunity:&lt;/strong&gt;&lt;/p&gt;
セルジオタピア

この場合、.innerHTMLを使用するか、HTMLEntitiesをデコードする必要があります。
Ilanus

タグ付きの複数行またはHTMLコードを返します。function htmlDecode(input){var e = document.createElement( 'div'); e.innerHTML = input; 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; }} return 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のdangerouslySetInnerHTMLを使用する必要がありません。HTML文字列をReactコンポーネントに変換するユーティリティです。dangerouslySetInnerHTMLの使用を回避し、標準のHTML要素、属性、インラインスタイルを対応するReactに変換します。


11
このライブラリはバックグラウンドで「dangerouslySetInnerHTML」を使用しますか?
Omar

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 2018

3
ええ、技術的にはあなたは正しい@mrkvonですが、前述のように、このソリューションは、「html」/ jsxがユーザーが制御できるものである場合にのみ有効です。たとえば、APIを介して提供される未加工の html をレンダリングするためではありません。FragmentAPIが登場する以前は、常に面倒でした。そのため、追加のspanラップが必要になり、場合によってはフレックスレイアウトが混乱することがありました。可能な解決策を探すためにこの質問に出くわしたとき、私は物事を回避する方法共有したいと思いました。
Brad Adams

2
ありがとう!これが私の場合に機能した唯一のソリューションでした。また、この回答に対するmrkvonのコメントへの返信:この回答には実際にhtml Some text <strong>wrapped with strong</strong>が含まれていますstrong。つまり、htmlタグが含まれています。
Binita Bharati

@BinitaBharatiしかし、それは文字列ではありません。「<p> This is a String </ p>」などのAPIから文字列を取得した場合(または単に文字列を変数に格納した場合)、この文字列を<Fragment>に配置しても、出力には引き続き< p>タグ。
ムクデカール

1
@BradAdams。素敵なトリックですが。重宝する場面がわかります。
ムクデカル


5

dangerouslySetInnerHTML

dangerouslySetInnerHTMLは、ブラウザのDOMでinnerHTMLを使用するためのReactの代替品です。一般に、ユーザーが誤ってクロスサイトスクリプティング(XSS)攻撃にさらされる可能性があるため、コードからHTMLを設定することは危険です。したがって、Reactから直接HTMLを設定できますが、危険であることを思い出させるために、dangerouslySetInnerHTMLと入力して__htmlキーでオブジェクトを渡す必要があります。例えば:

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

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

3

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

私はこれが好きです、あなたがその贅沢を持っていないとき、追加のライブラリやサーバー側に依存する必要はありません。あなたに触発されましたが、クラスのコンポーネントでは、 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 buildreact-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}
);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.