Rendre la chaîne HTML en tant que vrai HTML dans un composant React


163

Voici ce que j'ai essayé et comment ça va mal.

Cela marche:

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

Cela ne:

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

La propriété description est simplement une chaîne normale de contenu HTML. Cependant, il est rendu sous forme de chaîne et non de HTML pour une raison quelconque.

entrez la description de l'image ici

Aucune suggestion?

Réponses:


51

Vérifiez si le texte que vous essayez d'ajouter au nœud n'est pas échappé comme ceci:

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

Au lieu de cela:

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

si est échappé, vous devez le convertir depuis votre serveur.

Le nœud est du texte car il est échappé

Le nœud est du texte car il est échappé

Le nœud est un nœud dom car il n'est pas échappé

Le nœud est un nœud dom car il n'est pas échappé


3
C'était le problème. La chaîne de description a été échappée en HTML. Je ne l'ai pas échappé et maintenant cela fonctionne très bien.
Sergio Tapia le

4
Veuillez éviter d'utiliser à la dangerouslySetInnerHTMLplace l'utilisation Fragmentde react v16. Vérifiez la prochaine réponse de @ brad-adams
Kunal Parekh

2
J'apprécie la mention @KunalParekh, mais ce sont des choses différentes. Ma réponse n'est valable que si le html est situé dans votre application (ce qui signifie qu'il s'agit en fait de JSX). Pour analyser le HTML d'une source externe vers jsx, vous devez rechercher une autre solution.
Brad Adams

114

Est this.props.match.description-ce qu'une chaîne ou un objet? S'il s'agit d'une chaîne, elle doit être convertie en HTML très bien. Exemple:

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

Résultat: http://codepen.io/ilanus/pen/QKgoLA?editors=1011

Cependant, si description: <h1 style="color:red;">something</h1>sans les citations, ''vous obtiendrez:

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

S'il s'agit d'une chaîne et que vous ne voyez aucun balisage HTML, le seul problème que je vois est un balisage incorrect.

METTRE À JOUR

Si vous traitez avec HTMLEntitles. Vous devez les décoder avant de les envoyer, dangerouslySetInnerHTMLc'est pourquoi ils l'ont appelé dangereusement :)

Exemple de travail:

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

this.props.match.descriptionest une chaîne, pas un objet. Que voulez-vous dire par un mauvais balisage? Voulez-vous dire des tags non fermés? React devrait simplement le rendre non?
Sergio Tapia

Pourriez-vous coller ici console.log (this.props.match.description);
Ilanus

Un exemple:&lt;p&gt;&lt;strong&gt;Our Opportunity:&lt;/strong&gt;&lt;/p&gt;
Sergio Tapia

Dans ce cas, vous devez utiliser .innerHTML ou décoder HTMLEntities.
Ilanus

Renvoie plusieurs lignes ou du code HTML avec des balises: function htmlDecode (input) {var e = document.createElement ('div'); e.innerHTML = entrée; var returnString = ''; for (index = 0; index <e.childNodes.length; index ++) {// cas d'une seule chaîne if (e.childNodes [index] .nodeValue) {returnString + = e.childNodes [index] .nodeValue; } // cas du HTML if (e.childNodes [index] .outerHTML) {returnString + = e.childNodes [index] .outerHTML; }} return returnString; }
Chris Adams le

59

J'utilise 'react-html-parser'

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

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

Source sur npmjs.com

Soulevez le commentaire de @ okram pour plus de visibilité:

à partir de sa description github: convertit les chaînes HTML directement en composants React évitant d'avoir à utiliser dangerouslySetInnerHTML de npmjs.com Un utilitaire pour convertir des chaînes HTML en composants React. Évite l'utilisation de dangerouslySetInnerHTML et convertit les éléments HTML standard, les attributs et les styles en ligne en leurs équivalents React.


11
Cette bibliothèque utilise-t-elle "dangerouslySetInnerHTML" en arrière-plan?
Omar

1
à partir de sa description 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 le

15

Si vous avez le contrôle sur la provenance de la chaîne contenant le HTML (c'est-à-dire quelque part dans votre application), vous pouvez bénéficier de la nouvelle <Fragment>API, en faisant quelque chose comme:

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

11
Il n'y a pas de chaîne contenant du html dans votre exemple. C'est soit jsx, soit une chaîne simple.
mrkvon

3
Eh bien, ouais techniquement, vous avez raison @mrkvon, mais comme je le dis, cette solution n'est valable que si vous avez le contrôle sur "html" / jsx. Pas pour le rendu de certains html bruts fournis via une API, par exemple. Avant l' FragmentAPI, c'était toujours une douleur pour moi, cela nécessitait des spanenveloppes supplémentaires qui gênaient parfois les dispositions flexibles. Quand je suis tombé sur cette question à la recherche d'une solution possible, j'ai pensé partager comment je contournais les choses.
Brad Adams

2
Merci! C'était la seule solution qui fonctionnait dans mon cas. Aussi, en réponse au commentaire de mrkvon sur cette réponse: Cette réponse contient en effet du html, c'est Some text <strong>wrapped with strong</strong>-à- dire contient une balise html strong.
Binita Bharati

@BinitaBharati Mais ce n'est pas une chaîne. Si vous obtenez une chaîne d'une API comme "<p> Ceci est une chaîne </p>" (ou stockez simplement une chaîne dans une variable), lorsque vous mettez cette chaîne dans <Fragment>, la sortie contiendra toujours le < balise p>.
Muchdecal

1
@BradAdams. Belle astuce cependant. Je peux voir les cas où cela devient pratique.
Muchdecal


6

dangereusementSetInnerHTML

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). Ainsi, vous pouvez définir HTML directement à partir de React, mais vous devez taper dangerouslySetInnerHTML et passer un objet avec une clé __html, pour vous rappeler que c'est dangereux. Par exemple:

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

function MyComponent() {
  return <div dangerouslySetInnerHTML={createMarkup()} />;
}

3

J'utilise innerHTML avec une référence pour couvrir:

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

J'aime ça, pas besoin de bibliothèques supplémentaires ni de dépendance côté serveur lorsque vous n'avez pas ce luxe. Inspiré par vous, mais dans un composant de classe que j'ai fait, componentDidMount() { this.message.current.innerHTML = this.state.selectedMessage.body; }body est le html échappé pour moi.
webhound

2

Dans mon cas, j'ai utilisé react-render-html

Installez d'abord le package en npm i --save react-render-html

puis,

import renderHTML from 'react-render-html';

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

1

Je n'ai pas pu npm buildtravailler avec react-html-parser. Cependant, dans mon cas, j'ai pu utiliser avec succès https://reactjs.org/docs/fragments.html . J'avais besoin d'afficher quelques caractères unicode html, mais ils ne devraient pas être directement intégrés dans le JSX. Dans le JSX, il devait être sélectionné dans l'état du composant. L'extrait de code du composant est donné ci-dessous:

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


-2

Si vous avez le contrôle sur {this.props.match.description} et si vous utilisez JSX. Je recommanderais de ne pas utiliser "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}
);
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.