Rendu html brut avec reactjs

154

Alors, est-ce la seule façon de rendre du HTML brut avec 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>
    );
  }
});

Je sais qu'il existe des moyens sympas de baliser des trucs avec JSX, mais je suis principalement intéressé par la possibilité de rendre du html brut (avec toutes les classes, styles en ligne, etc.). Quelque chose de compliqué comme ça:

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

Je ne voudrais pas avoir à réécrire tout cela dans JSX.

Peut-être que je pense à tout cela mal. S'il vous plaît corrigez-moi.

vinhboy
la source
1
C'est presque JSX. Si vous affichez beaucoup de balisage au format HTML brut, vous perdez l'avantage d'utiliser une bibliothèque comme React. Je recommanderais de faire les petits changements (comme "class" -> "className") pour laisser React gérer les éléments.
Ross Allen
2
Pour cet exemple spécifique, quelqu'un a déjà fait le travail pour vous , mais la question reste valable pour le cas général.
Randy Morris le

Réponses:

43

Vous pouvez html-to-reactutiliser le module npm.

Remarque: je suis l'auteur du module et je viens de le publier il y a quelques heures. N'hésitez pas à signaler tout bogue ou problème d'utilisation.

Mike Nikles
la source
24
utilise-t-il dangereusementSetInnerHTML en interne?
Yash Sharma
2
Je viens de vérifier le code. Il ne semble pas utiliser dangerouslySetInnerHTML.
Ajay Raghav
Mike maintenez-vous toujours activement ce module npm?
Daniel
Hey Daniel, je n'ai pas et l'un des contributeurs l'a repris et a publié la dernière version il y a 7 mois. Voir github.com/aknuds1/html-to-react
Mike Nikles
Cela n'utilise aucun dangerouslySetInnerHTML.
Tessaracter
186

Il existe désormais des méthodes plus sûres pour rendre le HTML. J'ai couvert cela dans une réponse précédente ici . Vous avez 4 options, les dernières utilisations dangerouslySetInnerHTML.

Méthodes de rendu HTML

  1. Le plus simple - Utilisez Unicode, enregistrez le fichier au format UTF-8 et définissez le charsetsur UTF-8.

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

  2. Plus sûr - Utilisez le numéro Unicode de l'entité dans une chaîne Javascript.

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

    ou

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

  3. Ou un tableau mixte avec des chaînes et des éléments JSX.

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

  4. Dernier recours - Insérez du HTML brut en utilisant dangerouslySetInnerHTML.

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

Brett DeWoody
la source
Tout ce dont j'ai besoin c'est de 3 ou 4
Nicolas S.Xu
se demander ce que les autres attributs reçoivent en dangerouslySetInnerHTMLplus de __html
Juan Solano
30
J'adore ces fils ... Implicitement: "Faites tout ce que vous pouvez pour ne pas utiliser le # 4". Tout le monde: "# 4 a très bien fonctionné!"
deepelement
1
@JuanSolano aucun, selon les entrées de saisie semi-automatique dans un environnement TypeScript.
Andreas Linnert le
Dans une question similaire, stackoverflow.com/questions/19266197/… , cette réponse n'a pas fait trop bien. Peut-être que cela convient mieux à cette question ou peut-être que les gens ne lisent pas la réponse ...: D
425nesp
27

J'ai utilisé ceci dans des situations rapides et sales:

// 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
la source
7
Ce n'est pas sûr - et si le HTML contient <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" onload="alert('test');">?
yjwong
11
C'est sûr si vous contrôlez le rendu HTML
John
18

J'ai essayé ce composant pur:

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

Caractéristiques

  • Prend un classNameaccessoire (plus facile à coiffer)
  • Remplace \npar <br />(vous voulez souvent faire cela)
  • Placez le contenu en tant qu'enfants lors de l'utilisation du composant comme:
  • <RawHTML>{myHTML}</RawHTML>

J'ai placé le composant dans un Gist sur Github: RawHTML: composant pur ReactJS pour rendre le HTML

Netsi1964
la source
13

dangerouslySetInnerHTML est le remplacement de React pour l'utilisation de innerHTML dans le DOM du navigateur. En général, définir du code HTML à partir du code est risqué car il est facile d'exposer par inadvertance vos utilisateurs à une attaque de script intersite (XSS).

Il est préférable / plus sûr de nettoyer votre HTML brut (en utilisant par exemple DOMPurify) avant de l'injecter dans le DOM via dangerouslySetInnerHTML.

DOMPurify - un désinfectant XSS ultra-rapide et ultra-tolérant pour le DOM uniquement pour HTML, MathML et SVG. DOMPurify fonctionne avec une valeur par défaut sécurisée, mais offre beaucoup de configurations et de crochets.

Exemple :

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
la source
C'est exactement ce que je recherchais dans cette réponse. Il devrait avoir plus de votes positifs
llamerr
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
la source
Le travail ci-dessus pour moi, je passais du html au corps modal.
Jozcar
6

J'ai utilisé cette bibliothèque appelée Parser . Cela a fonctionné pour ce dont j'avais besoin.

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

class MyComponent extends Component {
  render() {
    <div>{Parser(this.state.message)}</div>
  }
};
écairol
la source
3
21 Ko (8 Ko gzippé). Je ne pouvais pas justifier cela pour le code du navigateur.
Peter Bengtsson
cette bibliothèque casse mon application Expo
ekkis
5

dangerouslySetInnerHTMLne doit pas être utilisé sauf en cas de nécessité absolue. Selon la documentation, "Ceci est principalement pour coopérer avec les bibliothèques de manipulation de chaînes DOM" . Lorsque vous l'utilisez, vous renoncez au bénéfice de la gestion DOM de React.

Dans votre cas, il est assez simple de convertir une syntaxe JSX valide; changez simplement les classattributs en className. Ou, comme mentionné dans les commentaires ci-dessus, vous pouvez utiliser la bibliothèque ReactBootstrap qui encapsule les éléments Bootstrap dans les composants React.

Breno Ferreira
la source
8
Merci pour votre réponse. Je comprends les implications de sécurité de l'utilisation de innerHTML et je sais que je peux le convertir en JSX. Mais je pose spécifiquement des questions sur la prise en charge de React pour l'utilisation d'extraits de code HTML bruts.
vinhboy
Qu'entendez-vous exactement par "Prise en charge de React pour l'utilisation d'extraits de code HTML bruts"?
Breno Ferreira
2

Voici une version un peu moins avisée de la fonction RawHTML publiée auparavant. Cela vous permet:

  • configurer le tag
  • remplacer éventuellement les nouvelles lignes à <br />l »
  • transmettre des accessoires supplémentaires que RawHTML transmettra à l'élément créé
  • fournir une chaîne vide ( RawHTML></RawHTML>)

Voici le composant:

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

Usage:

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

Production:

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

Il se cassera sur:

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

J'avais besoin d'utiliser un lien avec l'attribut onLoad dans ma tête où div n'est pas autorisé, donc cela m'a causé une douleur importante. Ma solution de contournement actuelle consiste à fermer la balise de script d'origine, à faire ce que je dois faire, puis à ouvrir la balise de script (à fermer par l'original). J'espère que cela pourrait aider quelqu'un qui n'a absolument pas d'autre choix:

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