Renderuj ciąg HTML jako prawdziwy HTML w komponencie React


159

Oto, czego próbowałem i jak idzie źle.

To działa:

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

To nie:

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

Właściwość description jest zwykłym ciągiem zawartości HTML. Jednak z jakiegoś powodu jest renderowany jako ciąg znaków, a nie jako HTML.

wprowadź opis obrazu tutaj

Jakieś sugestie?

Odpowiedzi:


50

Sprawdź, czy tekst, który próbujesz dołączyć do węzła, nie zawiera znaków ucieczki w następujący sposób:

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

Zamiast tego:

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

jeśli jest uciekany, powinieneś przekonwertować go po stronie serwera.

Węzeł jest tekstowy, ponieważ został zmieniony

Węzeł jest tekstowy, ponieważ został zmieniony

Węzeł jest węzłem dom, ponieważ nie ma ucieczki

Węzeł jest węzłem dom, ponieważ nie ma ucieczki


3
To był problem. Ciąg opisu został zastąpiony kodem HTML. Usunąłem to i teraz działa dobrze.
Sergio Tapia

4
Prosimy o unikanie używania dangerouslySetInnerHTMLzamiast tego używaj Fragmentodreaktyw v16. Sprawdź następną odpowiedź @ brad-adams
Kunal Parekh

2
Doceń wzmiankę @KunalParekh, ale to różne rzeczy. Moja odpowiedź jest prawidłowa tylko wtedy, gdy html znajduje się w Twojej aplikacji (co oznacza, że ​​jest to faktycznie JSX). Aby przeanalizować HTML z zewnętrznego źródła do jsx, musisz poszukać innego rozwiązania.
Brad Adams,

113

Czy this.props.match.descriptionjest ciągiem czy przedmiotem? Jeśli jest to ciąg znaków, powinien zostać przekonwertowany na HTML. Przykład:

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

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

Jeśli jednak description: <h1 style="color:red;">something</h1>bez cudzysłowów ''otrzymasz:

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

Jeśli jest to ciąg i nie widzisz żadnych znaczników HTML, jedyny problem, jaki widzę, to nieprawidłowe znaczniki.

AKTUALIZACJA

Jeśli masz do czynienia z HTMLEntitles. Musisz je zdekodować przed wysłaniem dodangerouslySetInnerHTML , dlatego nazwali to niebezpiecznie :)

Przykład pracy:

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.descriptionjest łańcuchem, a nie obiektem. Co masz na myśli mówiąc o niewłaściwym oznaczeniu? Masz na myśli niezamknięte tagi? React powinien po prostu sprawić, że nie?
Sergio Tapia

Czy mógłbyś wkleić tutaj console.log (this.props.match.description);
Ilanus

Jeden przykład:&lt;p&gt;&lt;strong&gt;Our Opportunity:&lt;/strong&gt;&lt;/p&gt;
Sergio Tapia

W takim przypadku musisz użyć .innerHTML lub zdekodować HTMLEntities.
Ilanus

Zwróć wiele wierszy lub kod HTML z tagami: function htmlDecode (input) {var e = document.createElement ('div'); e.innerHTML = input; var returnString = ''; for (index = 0; index <e.childNodes.length; index ++) {// przypadek samego ciągu if (e.childNodes [index] .nodeValue) {returnString + = e.childNodes [index] .nodeValue; } // wielkość liter HTML if (e.childNodes [index] .outerHTML) {returnString + = e.childNodes [index] .outerHTML; }} return returnString; }
Chris Adams

58

Używam parsera „react-html-parser”

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

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

Źródło na npmjs.com

Podnoszę komentarz @ okram, aby uzyskać lepszą widoczność:

z jego opisu na githubie: Konwertuje ciągi HTML bezpośrednio na komponenty Reacta, unikając konieczności używania niebezpiecznego zestawu InsetInnerHTML z npmjs.com Narzędzie do konwersji łańcuchów HTML na komponenty React. Unika używania niebezpiecznego zestawu elementówInnerHTML i konwertuje standardowe elementy HTML, atrybuty i style wbudowane na ich odpowiedniki w React.


11
Czy ta biblioteka używa w tle parametru „dangerousouslySetInnerHTML”?
Omar

1
z opisu Converts HTML strings directly into React components avoiding the need to use dangerouslySetInnerHTMLna githubie : z 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

Jeśli masz kontrolę nad tym, skąd pochodzi ciąg znaków zawierający html (tj. Gdzieś w Twojej aplikacji), możesz skorzystać z nowego <Fragment>interfejsu API, wykonując coś takiego:

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
W Twoim przykładzie nie ma łańcucha zawierającego html. Jest to jsx lub zwykły ciąg.
mrkvon

3
Cóż, tak, technicznie rzecz biorąc, masz rację @mrkvon, jednak jak już wspomniałem, to rozwiązanie jest poprawne tylko wtedy, gdy napisane "html" / jsx jest czymś, nad czym masz kontrolę. Na przykład nie do renderowania surowego kodu HTML dostarczonego przez interfejs API. Przed FragmentAPI zawsze było to dla mnie uciążliwe, co wymagało dodatkowych spanokładek, które czasami zepsuły układy flex. Kiedy natknąłem się na to pytanie, szukając możliwego rozwiązania, pomyślałem, że podzielę się tym , jak sobie poradziłem.
Brad Adams

2
Dzięki! To było jedyne rozwiązanie, które zadziałało w moim przypadku. Odpowiadając również na komentarz mrkvona do tej odpowiedzi: Ta odpowiedź rzeczywiście zawiera html, czyli Some text <strong>wrapped with strong</strong>zawiera znacznik html strong.
Binita Bharati

@BinitaBharati Ale to nie jest ciąg. Jeśli otrzymujesz ciąg z interfejsu API, taki jak „<p> To jest ciąg </p>” (lub po prostu przechowujesz ciąg w zmiennej), po umieszczeniu tego ciągu w <Fragment> dane wyjściowe nadal będą zawierać < p> tag.
Muchdecal

1
@BradAdams. Niezła sztuczka. Widzę przypadki, w których staje się to przydatne.
Muchdecal


5

niebezpiecznieSetInnerHTML

niebezpiecznieSetInnerHTML jest zamiennikiem Reacta dla używania innerHTML w DOM przeglądarki. Ogólnie rzecz biorąc, ustawienie kodu HTML z kodu jest ryzykowne, ponieważ łatwo jest nieumyślnie narazić użytkowników na atak typu cross-site scripting (XSS). Możesz więc ustawić HTML bezpośrednio z Reacta, ale musisz niebezpiecznie wpisać SetInnerHTML i przekazać obiekt z kluczem __html, aby przypomnieć sobie, że jest niebezpieczny. Na przykład:

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

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

3

Używam innerHTML razem z ref to span:

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

Podoba mi się to, nie ma potrzeby posiadania dodatkowych bibliotek ani polegania na serwerze, gdy nie masz tego luksusu. Zainspirowany Tobą, ale w komponencie klasy, który zrobiłem, componentDidMount() { this.message.current.innerHTML = this.state.selectedMessage.body; }body to dla mnie uciekający kod HTML.
webhound

2

W moim przypadku użyłem reakcji-render-html

Najpierw zainstaluj pakiet przez npm i --save react-render-html

następnie,

import renderHTML from 'react-render-html';

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

1

Nie mogłem się zabrać npm builddo pracy react-html-parser. Jednak w moim przypadku udało mi się z powodzeniem skorzystać z https://reactjs.org/docs/fragments.html . Miałem wymóg, aby pokazać kilka znaków Unicode HTML, ale nie powinny one być bezpośrednio osadzane w JSX. W JSX musiało zostać pobrane ze stanu komponentu. Fragment kodu komponentu znajduje się poniżej:

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

Jeśli masz kontrolę do {this.props.match.description} i jeśli używasz JSX. Zalecałbym, aby nie używać „niebezpiecznieSetInnerHTML”.

// 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}
);
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.