Rendering della stringa HTML come HTML reale in un componente React


162

Ecco cosa ho provato e come va storto.

Questo funziona:

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

Questo non:

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

La proprietà description è solo una normale stringa di contenuto HTML. Tuttavia è reso come una stringa, non come HTML per qualche motivo.

inserisci qui la descrizione dell'immagine

Eventuali suggerimenti?

Risposte:


51

Controlla se il testo che stai tentando di aggiungere al nodo non è sfuggito in questo modo:

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

Invece di questo:

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

se è scappato dovresti convertirlo dal tuo lato server.

Il nodo è testo perché è sfuggito

Il nodo è testo perché è sfuggito

Il nodo è un nodo dom perché non è sottoposto a escape

Il nodo è un nodo dom perché non è sottoposto a escape


3
Questo era il problema. La stringa di descrizione è stata esclusa dall'HTML. L'ho lasciato sfuggire e ora funziona benissimo.
Sergio Tapia,

4
Si prega di evitare di utilizzare dangerouslySetInnerHTMLinvece utilizzare Fragmentda reagire v16. Controlla la risposta successiva di @ brad-adams
Kunal Parekh

2
Apprezzo la menzione @KunalParekh, ma sono cose diverse. La mia risposta è valida solo se l'html si trova all'interno della tua app (significa che in realtà è JSX). Per analizzare HTML da una fonte esterna a jsx dovresti cercare un'altra soluzione.
Brad Adams,

113

Non this.props.match.descriptionè una stringa o un oggetto? Se è una stringa, dovrebbe essere convertita in HTML bene. Esempio:

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

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

Tuttavia se description: <h1 style="color:red;">something</h1>senza le virgolette ''otterrai:

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

Se è una stringa e non vedi alcun markup HTML, l'unico problema che vedo è il markup sbagliato.

AGGIORNARE

Se hai a che fare con HTMLEntitles. Devi decodificarli prima di inviarli a dangerouslySetInnerHTMLquesto è il motivo per cui lo hanno chiamato pericolosamente :)

Esempio funzionante:

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è una stringa, non un oggetto. Cosa intendi con markup sbagliato? Intendi i tag non chiusi? React dovrebbe semplicemente renderlo no?
Sergio Tapia,

Potresti incollare qui console.log (this.props.match.description);
Ilanus,

Un esempio:&lt;p&gt;&lt;strong&gt;Our Opportunity:&lt;/strong&gt;&lt;/p&gt;
Sergio Tapia,

In questo caso è necessario utilizzare .innerHTML o decodificare HTMLEntities.
Ilanus,

Restituisce più righe o codice HTML con tag: funzione htmlDecode (input) {var e = document.createElement ('div'); e.innerHTML = input; var returnString = ''; for (index = 0; index <e.childNodes.length; index ++) {// caso di una sola stringa if (e.childNodes [index] .nodeValue) {returnString + = e.childNodes [index] .nodeValue; } // caso di HTML if (e.childNodes [indice] .outerHTML) {returnString + = e.childNodes [indice] .outerHTML; }} return returnString; }
Chris Adams,

58

Uso 'reagire-html-parser'

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

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

fonte su npmjs.com

Alzando il commento di @ okram per una maggiore visibilità:

dalla sua descrizione github: converte le stringhe HTML direttamente in componenti React evitando la necessità di usare pericolosamenteSetInnerHTML da npmjs.com Un'utilità per convertire stringhe HTML in componenti React. Evita l'uso di dangerouslySetInnerHTML e converte elementi HTML standard, attributi e stili incorporati nei loro equivalenti React.


11
Questa libreria utilizza "dangerouslySetInnerHTML" in background?
Omar,

1
dalla sua descrizione github: Converts HTML strings directly into React components avoiding the need to use dangerouslySetInnerHTMLda 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

15

Se hai il controllo da dove proviene la stringa contenente html (ad es. Da qualche parte nella tua app), puoi beneficiare della nuova <Fragment>API, facendo qualcosa del tipo:

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
Non esiste una stringa contenente HTML nel tuo esempio. È jsx o stringa semplice.
Mrkvon,

3
Beh, sì, tecnicamente hai ragione @mrkvon, tuttavia, come ho già detto, questa soluzione è valida solo se detto "html" / jsx è qualcosa su cui hai il controllo. Non per rendere alcuni greggio html fornito tramite API, ad esempio. Prima Fragmentdell'API era sempre una seccatura per me, che richiedeva spaninvolucri aggiuntivi che a volte rovinavano i layout flessibili. Quando mi sono imbattuto in questa domanda alla ricerca di una possibile soluzione, ho pensato di condividere come ho aggirato le cose.
Brad Adams,

2
Grazie! Questa è stata l'unica soluzione che ha funzionato nel mio caso. Inoltre, rispondendo al commento di mrkvon su questa risposta: questa risposta contiene effettivamente html, ovvero Some text <strong>wrapped with strong</strong>contiene tag html strong.
Binita Bharati,

@BinitaBharati Ma questa non è una stringa. Se ottieni una stringa da un'API come "<p> Questa è una stringa </p>" (o semplicemente memorizzi una stringa in una variabile), quando inserisci questa stringa in <Fragment>, l'output conterrà comunque < p> tag.
Muchdecal

1
@BradAdams. Bel trucco però. Vedo i casi in cui diventa utile.
Muchdecal


6

dangerouslySetInnerHTML

dangerouslySetInnerHTML è il sostituto di React per l'utilizzo di innerHTML nel DOM del browser. In generale, impostare HTML dal codice è rischioso perché è facile esporre inavvertitamente gli utenti a un attacco di cross-site scripting (XSS). Quindi, puoi impostare HTML direttamente da React, ma devi digitare pericolosamenteSetInnerHTML e passare un oggetto con una chiave __html, per ricordare a te stesso che è pericoloso. Per esempio:

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

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

3

Uso innerHTML insieme come riferimento per comprendere:

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

Mi piace questo, non c'è bisogno di ulteriori librerie o di affidamento sul lato server quando non hai quel lusso. Ispirato da te, ma in un componente di classe che ho fatto componentDidMount() { this.message.current.innerHTML = this.state.selectedMessage.body; }body è l'html sfuggito per me.
webhound

2

Nel mio caso, ho usato reagire-render-html

Prima installa il pacchetto entro npm i --save react-render-html

poi,

import renderHTML from 'react-render-html';

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

1

Non ho potuto npm buildlavorare con react-html-parser. Tuttavia, nel mio caso, sono stato in grado di utilizzare correttamente https://reactjs.org/docs/fragments.html . Avevo l'obbligo di mostrare pochi caratteri HTML unicode, ma non dovevano essere direttamente incorporati in JSX. All'interno di JSX, doveva essere prelevato dallo stato del componente. Lo snippet di codice componente è riportato di seguito:

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

Se hai il controllo su {this.props.match.description} e se stai utilizzando JSX. Consiglierei di non usare "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}
);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.