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.


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.


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


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

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


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

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

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

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.


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>

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>`,}}/>
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.