Renderizar string HTML como HTML real em um componente React


159

Aqui está o que eu tentei e como dá errado.

Isso funciona:

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

Isto não:

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

A propriedade description é apenas uma sequência normal de conteúdo HTML. No entanto, é renderizado como uma sequência, não como HTML por algum motivo.

insira a descrição da imagem aqui

Alguma sugestão?

Respostas:


50

Verifique se o texto que você está tentando anexar ao nó não é escapado assim:

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

Em vez disso:

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

se for escapado, você deve convertê-lo do seu lado do servidor.

O nó é texto porque escapou

O nó é texto porque escapou

O nó é um nó dom porque não é escapado

O nó é um nó dom porque não é escapado


3
Esse foi o problema. A cadeia de descrição foi um escape de HTML. Eu escapei e agora funciona bem.
Sergio Tapia

4
Evite usar, em dangerouslySetInnerHTMLvez disso, use a Fragmentpartir do react v16. Verifique a próxima resposta por @ brad-adams
Kunal Parekh

2
Aprecie a menção @KunalParekh, mas são coisas diferentes. Minha resposta é válida apenas se o html estiver localizado no seu aplicativo (ou seja, na verdade, é JSX). Para analisar o HTML de uma fonte externa para o jsx, você precisa procurar outra solução.
Brad Adams

113

Será que this.props.match.descriptioné uma string ou um objeto? Se for uma string, ela deve ser convertida para HTML muito bem. Exemplo:

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

Resultado: http://codepen.io/ilanus/pen/QKgoLA?editors=1011

No entanto, se description: <h1 style="color:red;">something</h1>sem as aspas ''você receberá:

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

Se é uma string e você não vê nenhuma marcação HTML, o único problema que vejo é a marcação errada.

ATUALIZAR

Se você está lidando com HTMLEntitles. Você precisa decodificá-los antes de enviá-los paradangerouslySetInnerHTML isso é que eles chamam perigosamente :)

Exemplo de trabalho:

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.descriptioné uma string, não um objeto. O que você quer dizer com marcação errada? Você quer dizer tags não fechadas? Reagir deve torná-lo não?
Sergio Tapia

Você pode colar aqui console.log (this.props.match.description);
Ilanus 28/09/16

Um exemplo:&lt;p&gt;&lt;strong&gt;Our Opportunity:&lt;/strong&gt;&lt;/p&gt;
Sergio Tapia

Nesse caso, você precisa usar .innerHTML ou decodificar HTMLEntities.
Ilanus 28/09

Retorne várias linhas ou código HTML com tags: function htmlDecode (input) {var e = document.createElement ('div'); e.innerHTML = entrada; var returnString = ''; for (index = 0; index <e.childNodes.length; index ++) {// caso de apenas uma string if (e.childNodes [index] .nodeValue) {returnString + = e.childNodes [index] .nodeValue; } // caso de HTML if (e.childNodes [index] .outerHTML) {returnString + = e.childNodes [index] .outerHTML; }} return returnString; }
Chris Adams

58

Eu uso 'react-html-parser'

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

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

Fonte no npmjs.com

Levantando o comentário do @ okram para obter mais visibilidade:

a partir de sua descrição do github: converte seqüências de caracteres HTML diretamente em componentes do React, evitando a necessidade de usar o DangerouslySetInnerHTML de npmjs.com Um utilitário para converter seqüências de caracteres HTML em componentes do React. Evita o uso de DangerouslySetInnerHTML e converte elementos HTML padrão, atributos e estilos embutidos em seus equivalentes React.


11
Esta biblioteca usa "dangerouslySetInnerHTML" em segundo plano?
Omar

1
da descrição do github: Converts HTML strings directly into React components avoiding the need to use dangerouslySetInnerHTMLfrom 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 02/01

14

Se você tem controle sobre a origem da string que contém html (ou seja, em algum lugar do seu aplicativo), pode se beneficiar da nova <Fragment>API, fazendo algo como:

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
Não há nenhuma string contendo html no seu exemplo. É jsx ou string simples.
Mrkvon

3
Bem, sim, tecnicamente, você está correto @mrkvon, no entanto, como eu mencionei, essa solução só é válida se "html" / jsx for algo que você tem controle. Não para renderizar algum html bruto fornecido por meio de uma API, por exemplo. Antes da FragmentAPI, era sempre um problema para mim, que exigia spanenvoltórios adicionais que às vezes mexiam com layouts flexíveis. Quando me deparei com essa pergunta, procurando uma solução possível, pensei em compartilhar como me contornava.
Brad Adams

2
Obrigado! Esta foi a única solução que funcionou no meu caso. Além disso, respondendo ao comentário de mrkvon sobre esta resposta: Esta resposta realmente contém html, isto é, Some text <strong>wrapped with strong</strong>contém uma tag html strong.
Binita Bharati

@BinitaBharati Mas isso não é uma string. Se você obtiver uma string de uma API como "<p> Esta é uma String </p>" (ou simplesmente armazena uma string em uma variável), quando você coloca essa string em <Fragment>, a saída ainda contém o < p> tag.
Muchdecal 23/03

1
@BradAdams. Bom truque embora. Eu posso ver as instâncias em que isso se torna útil.
Muchdecal 24/03


5

dangerouslySetInnerHTML

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). Portanto, você pode definir o HTML diretamente do React, mas precisa digitar perigosamente o SetInnerHTML e passar um objeto com uma chave __html, para se lembrar de que é perigoso. Por exemplo:

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

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

3

Eu uso innerHTML juntos uma ref para abranger:

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

Gosto disso, não há necessidade de bibliotecas adicionais ou confiança no lado do servidor quando você não tem esse luxo. Inspirado por você, mas em um componente de classe que fiz componentDidMount() { this.message.current.innerHTML = this.state.selectedMessage.body; }body é o html de escape para mim.
webhound

2

No meu caso, usei react-render-html

Primeiro instale o pacote npm i --save react-render-html

então,

import renderHTML from 'react-render-html';

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

1

Não consegui npm buildtrabalhar react-html-parser. No entanto, no meu caso, consegui usar o https://reactjs.org/docs/fragments.html . Eu tinha um requisito para mostrar alguns caracteres unicode html, mas eles não deveriam ser incorporados diretamente no JSX. Dentro do JSX, ele teve que ser escolhido no estado do Componente. O snippet de código do componente é fornecido abaixo:

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

Se você tiver controle para {this.props.match.description} e se estiver usando JSX. Eu recomendaria não usar "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}
);
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.