Renderizar cadena HTML como HTML real en un componente React

163

Esto es lo que probé y cómo sale mal.

Esto funciona:

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

Esto no:

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

La propiedad de descripción es solo una cadena normal de contenido HTML. Sin embargo, se representa como una cadena, no como HTML por alguna razón.

ingrese la descripción de la imagen aquí

¿Alguna sugerencia?

Sergio Tapia
fuente

Respuestas:

51

Verifique si el texto que está intentando agregar al nodo no se escapa así:

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

En lugar de esto:

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

si se escapa, debe convertirlo desde el lado del servidor.

El nodo es texto porque se escapó

El nodo es texto porque se escapó

El nodo es un nodo dom porque no se escapa

El nodo es un nodo dom porque no se escapa

Sergio Flores
fuente
3
Este fue el problema. La cadena de descripción se escapó de HTML. Lo escapé y ahora funciona bien.
Sergio Tapia
44
Por favor, evite usar en dangerouslySetInnerHTMLlugar de usar Fragmentreact v16. Compruebe la próxima respuesta de @ brad-adams
Kunal Parekh
2
Agradezco la mención @KunalParekh, pero son cosas diferentes. Mi respuesta solo es válida si el html se encuentra dentro de su aplicación (lo que significa que en realidad es JSX). Para analizar HTML desde una fuente externa a jsx, deberá buscar otra solución.
Brad Adams
114

¿ this.props.match.descriptionEs una cadena o un objeto? Si es una cadena, debe convertirse a HTML perfectamente. Ejemplo:

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

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

Sin embargo, si description: <h1 style="color:red;">something</h1>sin las comillas ''vas a obtener:

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

Si es una cadena y no ve ningún marcado HTML, el único problema que veo es un marcado incorrecto.

ACTUALIZAR

Si se trata de títulos HTMLE. Debes decodificarlos antes de enviarlos dangerouslySetInnerHTML, por eso lo llamaron peligrosamente :)

Ejemplo de trabajo:

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'));
Ilanus
fuente
this.props.match.descriptiones una cadena, no un objeto. ¿Qué quieres decir con marcado incorrecto? ¿Te refieres a etiquetas no cerradas? Reaccionar solo debería hacer que no?
Sergio Tapia
¿Podría pegar aquí console.log (this.props.match.description);
Ilanus
Un ejemplo:&lt;p&gt;&lt;strong&gt;Our Opportunity:&lt;/strong&gt;&lt;/p&gt;
Sergio Tapia
En este caso, necesita usar .innerHTML o decodificar HTMLEntities.
Ilanus
Devuelve varias líneas o código HTML con etiquetas: función htmlDecode (input) {var e = document.createElement ('div'); e.innerHTML = input; var returnString = ''; for (index = 0; index <e.childNodes.length; index ++) {// caso de solo una cadena if (e.childNodes [index] .nodeValue) {returnString + = e.childNodes [index] .nodeValue; } // caso de HTML if (e.childNodes [index] .outerHTML) {returnString + = e.childNodes [index] .outerHTML; }} return returnString; }
Chris Adams
59

Yo uso 'react-html-parser'

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

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

Fuente en npmjs.com

Levantando el comentario de @ okram para mayor visibilidad:

de su descripción de github: Convierte cadenas HTML directamente en componentes React evitando la necesidad de usar peligrosamente SetInnerHTML de npmjs.com Una utilidad para convertir cadenas HTML en componentes React. Evita el uso de dangerouslySetInnerHTML y convierte elementos HTML estándar, atributos y estilos en línea en sus equivalentes React.

pixelearth
fuente
11
¿Esta biblioteca utiliza "dangerouslySetInnerHTML" en segundo plano?
Omar
1
de su descripción github: Converts HTML strings directly into React components avoiding the need to use dangerouslySetInnerHTMLde 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

Si tiene control sobre el origen de la cadena que contiene html (es decir, en algún lugar de su aplicación), puede beneficiarse de la nueva <Fragment>API, haciendo algo como:

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>
}
Brad Adams
fuente
11
No hay una cadena que contenga html en su ejemplo. Es jsx o una cadena simple.
mrkvon
3
Bueno, sí, técnicamente tienes razón @mrkvon, sin embargo, como mencioné, esta solución solo es válida si dicho "html" / jsx es algo sobre lo que tienes control. No para representar algunos html sin formato proporcionados a través de una API, por ejemplo. Antes de la FragmentAPI, siempre fue un dolor para mí, que requería spanenvolturas adicionales que a veces se complicaban con los diseños flexibles. Cuando me encontré con esta pregunta en busca de una posible solución que pensé en compartir la forma en que llegué alrededor de las cosas.
Brad Adams el
2
¡Gracias! Esta fue la única solución que funcionó en mi caso. Además, respondiendo al comentario de mrkvon sobre esta respuesta: Esta respuesta de hecho contiene html, es decir, Some text <strong>wrapped with strong</strong>contiene la etiqueta html strong.
Binita Bharati
@BinitaBharati Pero eso no es una cadena. Si obtiene una cadena de una API como "<p> Esta es una cadena </p>" (o simplemente almacena una cadena en una variable), cuando coloca esta cadena en <Fragmento>, la salida aún contendrá el < p> etiqueta.
Muchdecal
1
@BradAdams. Buen truco sin embargo. Puedo ver los casos en que se vuelve útil.
Muchdecal
6

dangerouslySetInnerHTML

dangerouslySetInnerHTML es el reemplazo de React para usar innerHTML en el navegador DOM. En general, establecer HTML a partir del código es arriesgado porque es fácil exponer a sus usuarios inadvertidamente a un ataque de scripting entre sitios (XSS). Por lo tanto, puede configurar HTML directamente desde React, pero debe escribir peligrosamente SetInnerHTML y pasar un objeto con una clave __html, para recordar que es peligroso. Por ejemplo:

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

function MyComponent() {
  return <div dangerouslySetInnerHTML={createMarkup()} />;
}
مهدی عابدی برنامه نویس و مشاور
fuente
3

Yo uso innerHTML juntos una referencia para abarcar:

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>
}
Lorenzo Delana
fuente
Me gusta esto, no es necesario tener bibliotecas adicionales ni depender del lado del servidor cuando no tienes ese lujo. Inspirado por ti, pero en un componente de clase que hice componentDidMount() { this.message.current.innerHTML = this.state.selectedMessage.body; }body es el html escapado para mí.
webhound
2

En mi caso, utilicé react-render-html

Primero instale el paquete por npm i --save react-render-html

luego,

import renderHTML from 'react-render-html';

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

No pude llegar npm builda trabajar react-html-parser. Sin embargo, en mi caso, pude utilizar con éxito https://reactjs.org/docs/fragments.html . Tenía el requisito de mostrar algunos caracteres HTML Unicode, pero no deberían incrustarse directamente en el JSX. Dentro del JSX, tuvo que ser elegido del estado del Componente. El fragmento de código de componente se proporciona a continuación:

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>);
}
Binita Bharati
fuente
0

yo uso https://www.npmjs.com/package/html-to-react

const HtmlToReactParser = require('html-to-react').Parser;
let htmlInput = html.template;
let htmlToReactParser = new HtmlToReactParser();
let reactElement = htmlToReactParser.parse(htmlInput); 
return(<div>{reactElement}</div>)
Анов Алексей
fuente
-2

Si tiene control sobre {this.props.match.description} y está utilizando JSX. Yo recomendaría no usar "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}
);
BBRay
fuente