Representación de HTML sin formato con reactjs

154

Entonces, ¿es esta la única forma de procesar html sin procesar con reactjs?

// http://facebook.github.io/react/docs/tutorial.html
// tutorial7.js
var converter = new Showdown.converter();
var Comment = React.createClass({
  render: function() {
    var rawMarkup = converter.makeHtml(this.props.children.toString());
    return (
      <div className="comment">
        <h2 className="commentAuthor">
          {this.props.author}
        </h2>
        <span dangerouslySetInnerHTML={{__html: rawMarkup}} />
      </div>
    );
  }
});

Sé que hay algunas formas geniales de marcar cosas con JSX, pero estoy principalmente interesado en poder procesar html sin formato (con todas las clases, estilos en línea, etc.). Algo complicado como este:

<!-- http://getbootstrap.com/components/#dropdowns-example -->
<div class="dropdown">
  <button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-expanded="true">
    Dropdown
    <span class="caret"></span>
  </button>
  <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu1">
    <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Action</a></li>
    <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Another action</a></li>
    <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Something else here</a></li>
    <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Separated link</a></li>
  </ul>
</div>

No me gustaría tener que reescribir todo eso en JSX.

Tal vez estoy pensando en todo esto mal. Por favor corrigeme.

vinhboy
fuente
1
Eso es casi JSX. Si representa muchas marcas como HTML sin formato, está perdiendo el beneficio de usar una biblioteca como React. Recomiendo hacer pequeños cambios (como "class" -> "className") para permitir que React maneje los elementos.
Ross Allen
2
Para este ejemplo específico, alguien ya ha hecho el trabajo por usted , sin embargo, la pregunta sigue siendo el caso general.
Randy Morris

Respuestas:

43

Puede aprovechar el html-to-reactmódulo npm.

Nota: Soy el autor del módulo y lo publiqué hace unas horas. No dude en informar cualquier error o problema de usabilidad.

Mike Nikles
fuente
24
¿utiliza peligrosamenteSetInnerHTML internamente?
Yash Sharma
2
Acabo de revisar el código. No parece estar usando peligrosamente SetInnerHTML.
Ajay Raghav
Mike, ¿sigues manteniendo activamente este módulo npm?
Daniel
Hola Daniel, no, y uno de los colaboradores se hizo cargo y publicó el último lanzamiento hace 7 meses. Ver github.com/aknuds1/html-to-react
Mike Nikles
Esto no utiliza ningún peligrosamente SetInnerHTML.
Tessaracter
185

Ahora hay métodos más seguros para representar HTML. Cubrí esto en una respuesta anterior aquí . Tienes 4 opciones, los últimos usos dangerouslySetInnerHTML.

Métodos para renderizar HTML

  1. Más fácil : use Unicode, guarde el archivo como UTF-8 y charsetconfigúrelo en UTF-8.

    <div>{'First · Second'}</div>

  2. Más seguro : use el número Unicode para la entidad dentro de una cadena Javascript.

    <div>{'First \u00b7 Second'}</div>

    o

    <div>{'First ' + String.fromCharCode(183) + ' Second'}</div>

  3. O una matriz mixta con cadenas y elementos JSX.

    <div>{['First ', <span>&middot;</span>, ' Second']}</div>

  4. Último recurso : inserte HTML sin formato utilizando dangerouslySetInnerHTML.

    <div dangerouslySetInnerHTML={{__html: 'First &middot; Second'}} />

Brett DeWoody
fuente
Todo lo que necesito es 3 o 4
Nicolas S.Xu
preguntándose qué otros atributos recibe dangerouslySetInnerHTMLademás de __html
Juan Solano
30
Me encantan estos hilos ... Implícitamente: "Haz lo que puedas para no usar el # 4". Todos: "# 4 funcionó muy bien!"
profundización
1
@JuanSolano none, según las entradas de autocompletar en un entorno TypeScript.
Andreas Linnert
En una pregunta similar, stackoverflow.com/questions/19266197/… , esta respuesta no funcionó demasiado bien. Tal vez sea mejor para esta pregunta o tal vez la gente no esté leyendo la respuesta ...: D
425nesp
27

He usado esto en situaciones rápidas y sucias:

// react render method:

render() {
    return (
      <div>
        { this.props.textOrHtml.indexOf('</') !== -1
            ? (
                <div dangerouslySetInnerHTML={{__html: this.props.textOrHtml.replace(/(<? *script)/gi, 'illegalscript')}} >
                </div>
              )
            : this.props.textOrHtml
          }

      </div>
      )
  }
Cory Robinson
fuente
77
Esto no es seguro, ¿y si el HTML contiene <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" onload="alert('test');">?
yjwong
11
Es seguro si tienes el control del HTML que se está procesando
John
18

He probado este componente puro:

const RawHTML = ({children, className = ""}) => 
<div className={className}
  dangerouslySetInnerHTML={{ __html: children.replace(/\n/g, '<br />')}} />

Caracteristicas

  • Toma classNameaccesorios (más fácil de peinar)
  • Sustituye \na <br />(a menudo quieres hacer eso)
  • Coloque contenido como hijos cuando use el componente como:
  • <RawHTML>{myHTML}</RawHTML>

He colocado el componente en un Gist en Github: RawHTML: componente puro ReactJS para renderizar HTML

Netsi1964
fuente
12
export class ModalBody extends Component{
    rawMarkup(){
        var rawMarkup = this.props.content
        return { __html: rawMarkup };
    }
    render(){
        return(
                <div className="modal-body">
                     <span dangerouslySetInnerHTML={this.rawMarkup()} />

                </div>
            )
    }
}
Jozcar
fuente
El trabajo anterior para mí, estaba pasando html al cuerpo modal.
Jozcar
12

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 sin darse cuenta a un ataque de secuencias de comandos entre sitios (XSS).

Es mejor / más seguro desinfectar su HTML sin formato (usando, por ejemplo, DOMPurify) antes de inyectarlo en el DOM a través de dangerouslySetInnerHTML.

DOMPurify : un desinfectante XSS súper rápido, súper rápido y solo para DOM para HTML, MathML y ​​SVG. DOMPurify funciona con un valor predeterminado seguro, pero ofrece mucha capacidad de configuración y ganchos.

Ejemplo :

import React from 'react'
import createDOMPurify from 'dompurify'
import { JSDOM } from 'jsdom'

const window = (new JSDOM('')).window
const DOMPurify = createDOMPurify(window)

const rawHTML = `
<div class="dropdown">
  <button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-expanded="true">
    Dropdown
    <span class="caret"></span>
  </button>
  <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu1">
    <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Action</a></li>
    <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Another action</a></li>
    <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Something else here</a></li>
    <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Separated link</a></li>
  </ul>
</div>
`

const YourComponent = () => (
  <div>
    { <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(rawHTML) }} /> }
  </div>
)

export default YourComponent
Yuci
fuente
Esto es exactamente lo que estaba buscando en esta respuesta. Debería haber más votos a favor
llamerr
6

Usé esta biblioteca llamada Parser . Funcionó para lo que necesitaba.

import React, { Component } from 'react';    
import Parser from 'html-react-parser';

class MyComponent extends Component {
  render() {
    <div>{Parser(this.state.message)}</div>
  }
};
ecairol
fuente
3
21 KB (8 KB comprimido). No podría justificar eso para el código del navegador.
Peter Bengtsson
esta biblioteca rompe mi aplicación Expo
ekkis
5

dangerouslySetInnerHTMLno debe usarse a menos que sea absolutamente necesario. Según los documentos, "Esto es principalmente para cooperar con las bibliotecas de manipulación de cadenas DOM" . Cuando lo usas, estás renunciando al beneficio de la administración DOM de React.

En su caso, es bastante sencillo convertir a una sintaxis JSX válida; solo cambia los classatributos a className. O, como se menciona en los comentarios anteriores, puede usar la biblioteca ReactBootstrap que encapsula elementos Bootstrap en componentes React.

Breno Ferreira
fuente
8
Gracias por tu respuesta. Entiendo las implicaciones de seguridad del uso de innerHTML y sé que puedo convertirlo a JSX. Pero estoy preguntando específicamente sobre el soporte de React para usar fragmentos de HTML sin formato.
vinhboy
¿Qué quiere decir exactamente con "soporte de React para usar fragmentos de HTML sin formato"?
Breno Ferreira
2

Aquí hay una versión menos obstinada de la función RawHTML publicada anteriormente. Te permite:

  • configurar la etiqueta
  • opcionalmente reemplazar nuevas líneas a <br />'s
  • pasar accesorios adicionales que RawHTML pasará al elemento creado
  • suministrar una cadena vacía ( RawHTML></RawHTML>)

Aquí está el componente:

const RawHTML = ({ children, tag = 'div', nl2br = true, ...rest }) =>
    React.createElement(tag, {
        dangerouslySetInnerHTML: {
            __html: nl2br
                ? children && children.replace(/\n/g, '<br />')
                : children,
        },
        ...rest,
    });
RawHTML.propTypes = {
    children: PropTypes.string,
    nl2br: PropTypes.bool,
    tag: PropTypes.string,
};

Uso:

<RawHTML>{'First &middot; Second'}</RawHTML>
<RawHTML tag="h2">{'First &middot; Second'}</RawHTML>
<RawHTML tag="h2" className="test">{'First &middot; Second'}</RawHTML>
<RawHTML>{'first line\nsecond line'}</RawHTML>
<RawHTML nl2br={false}>{'first line\nsecond line'}</RawHTML>
<RawHTML></RawHTML>

Salida:

<div>First · Second</div>
<h2>First · Second</h2>
<h2 class="test">First · Second</h2>
<div>first line<br>second line</div>
<div>first line
second line</div>
<div></div>

Se romperá en:

<RawHTML><h1>First &middot; Second</h1></RawHTML>
Christiaan Westerbeek
fuente
0

Necesitaba usar un enlace con el atributo onLoad en mi cabeza donde div no está permitido, así que esto me causó un dolor significativo. Mi solución actual es cerrar la etiqueta del script original, hacer lo que necesito hacer y luego abrir la etiqueta del script (para que el original la cierre). Espero que esto pueda ayudar a alguien que no tiene otra opción:

<script dangerouslySetInnerHTML={{ __html: `</script>
   <link rel="preload" href="https://fonts.googleapis.com/css?family=Open+Sans" as="style" onLoad="this.onload=null;this.rel='stylesheet'" crossOrigin="anonymous"/>
<script>`,}}/>
Adam Lane
fuente