Renderizando html bruto com reactjs

154

Então, esta é a única maneira de renderizar html bruto com 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>
    );
  }
});

Eu sei que existem algumas maneiras legais de marcar coisas com JSX, mas estou principalmente interessado em poder renderizar html bruto (com todas as classes, estilos embutidos, 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>

Eu não gostaria de reescrever tudo isso em JSX.

Talvez eu esteja pensando nisso tudo errado. Por favor me corrija.

vinhboy
fonte
1
Isso é quase JSX. Se você renderizar muitas marcações como HTML bruto, estará perdendo o benefício de usar uma biblioteca como o React. Eu recomendo fazer pequenas alterações (como "class" -> "className") para permitir que o React lide com os elementos.
Ross Allen
2
Para este exemplo específico, alguém já fez o trabalho para você , no entanto, a pergunta ainda se aplica ao caso geral.
Randy Morris

Respostas:

43

Você pode aproveitar o html-to-reactmódulo npm.

Nota: Sou o autor do módulo e o publiquei algumas horas atrás. Por favor, sinta-se livre para relatar quaisquer bugs ou problemas de usabilidade.

Mike Nikles
fonte
24
ele usa perigosamente o SetInnerHTML internamente?
Yash Sharma
2
Acabei de verificar o código. Não parece estar usando perigosamente o SetInnerHTML.
Ajay Raghav
Mike, você ainda está mantendo ativamente este módulo npm?
11119 Daniel
Ei Daniel, eu não e um dos colaboradores assumiu o cargo e publicou o último lançamento há 7 meses. Veja github.com/aknuds1/html-to-react
Mike Nikles
Isso não usa nenhum perigosamente SetInnerHTML.
Tessaracter 18/11/19
185

Agora existem métodos mais seguros para renderizar HTML. Eu cobri isso em uma resposta anterior aqui . Você tem 4 opções, os últimos usos dangerouslySetInnerHTML.

Métodos para renderizar HTML

  1. Mais fácil - use Unicode, salve o arquivo como UTF-8 e defina-o charsetcomo UTF-8.

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

  2. Mais seguro - use o número Unicode para a entidade dentro de uma string Javascript.

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

    ou

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

  3. Ou uma matriz mista com strings e elementos JSX.

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

  4. Último recurso - insira HTML bruto usando dangerouslySetInnerHTML.

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

Brett DeWoody
fonte
Todos necessidade I é 3 ou 4
Nicolas S.Xu
perguntando o que outros atributos recebe dangerouslySetInnerHTMLalém __html
Juan Solano
30
Eu amo esses tópicos ... Implicitamente: "Faça o que puder para não usar o número 4". Todos: "# 4 funcionou muito bem!"
deepelement 5/06/19
1
@JuanSolano nenhum, de acordo com as entradas de preenchimento automático em um ambiente TypeScript.
Andreas Linnert 17/02
Em uma pergunta semelhante, stackoverflow.com/questions/19266197/… , essa resposta não foi muito boa. Talvez seja um ajuste melhor para esta pergunta ou talvez as pessoas não estejam lendo a resposta ...: D
425nesp
27

Eu usei isso em situações rápidas e sujas:

// 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
fonte
7
Isso não é seguro - e se o HTML contiver <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" onload="alert('test');">?
Yjwong
11
É seguro se você está no controle do HTML que está sendo processado
John
18

Eu tentei este componente puro:

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

Recursos

  • Toma classNameprop (mais fácil de estilizar)
  • Substitui \npara <br />(você costuma fazer isso)
  • Coloque o conteúdo como filho ao usar o componente, como:
  • <RawHTML>{myHTML}</RawHTML>

Coloquei o componente em um Gist no Github: RawHTML: ReactJS puro componente para renderizar HTML

Netsi1964
fonte
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
fonte
O trabalho acima para mim, eu estava passando html para o corpo modal.
Jozcar
11

dangerouslySetInnerHTML é o substituto do React para o uso do innerHTML no DOM do navegador. Em geral, definir HTML a partir do código é arriscado, pois é fácil expor inadvertidamente seus usuários a um ataque de XSS (cross-site scripting).

É melhor / mais seguro higienizar seu HTML bruto (usando, por exemplo, DOMPurify) antes de injetá-lo no DOM via dangerouslySetInnerHTML.

DOMPurify - um desinfetante XSS exclusivo para DOM, super-rápido e tolerante a uber para HTML, MathML e SVG. O DOMPurify funciona com um padrão seguro, mas oferece muita configurabilidade e ganchos.

Exemplo :

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
fonte
Era exatamente isso que eu estava procurando nesta resposta. Deveria ter mais
votos positivos
6

Eu usei essa biblioteca chamada Parser . Funcionou para o que eu precisava.

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

class MyComponent extends Component {
  render() {
    <div>{Parser(this.state.message)}</div>
  }
};
ecairol
fonte
3
21 KB (8 KB compactados). Eu não poderia justificar isso para o código do navegador.
Peter Bengtsson
esta biblioteca quebra meu aplicativo Expo
ekkis 6/02
5

dangerouslySetInnerHTMLnão deve ser usado, a menos que seja absolutamente necessário. De acordo com os documentos, "Isso é principalmente para cooperar com as bibliotecas de manipulação de string do DOM" . Ao usá-lo, você está perdendo os benefícios do gerenciamento de DOM do React.

No seu caso, é bastante simples converter para sintaxe JSX válida; basta alterar os classatributos para className. Ou, como mencionado nos comentários acima, você pode usar a biblioteca ReactBootstrap que encapsula elementos do Bootstrap nos componentes do React.

Breno Ferreira
fonte
8
Obrigado pela sua resposta. Entendo as implicações de segurança do uso do innerHTML e sei que posso convertê-lo em JSX. Mas estou perguntando especificamente sobre o suporte do React para o uso de snippets HTML brutos.
vinhboy
O que exatamente você quer dizer com "Suporte do React ao uso de snippets HTML brutos"?
Breno Ferreira
2

Aqui está uma versão um pouco menos opinativa da função RawHTML publicada anteriormente. Permite:

  • configurar a tag
  • opcionalmente, substitua as novas linhas para <br /> 's
  • passar adereços extras que o RawHTML passará para o elemento criado
  • fornecer uma string vazia ( RawHTML></RawHTML>)

Aqui está o 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>

Resultado:

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

Irá quebrar:

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

Eu precisava usar um link com o atributo onLoad na minha cabeça, onde div não é permitido, então isso me causou uma dor significativa. Minha solução atual é fechar a tag de script original, fazer o que preciso e abrir a tag de script (a ser fechada pelo original). Espero que isso ajude alguém que não tem outra escolha:

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