Отобразить строку HTML как настоящий HTML в компоненте React


163

Вот что я попробовал и как все идет не так.

Это работает:

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

Это не:

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

Свойство description - это просто обычная строка содержимого HTML. Однако по какой-то причине он отображается как строка, а не как HTML.

введите описание изображения здесь

Какие-либо предложения?

Ответы:


51

Убедитесь, что текст, который вы пытаетесь добавить к узлу, не экранируется следующим образом:

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

Вместо этого:

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

если экранируется, вы должны конвертировать его со стороны сервера.

Узел текстовый, потому что экранированный

Узел текстовый, потому что экранированный

Узел является узлом dom, потому что не экранирован

Узел является узлом dom, потому что не экранирован


3
Это была проблема. Строка описания была экранирована HTML. Я удалился, и теперь он работает нормально.
Серхио Тапиа

4
Пожалуйста, избегайте использования dangerouslySetInnerHTMLвместо Fragmentреакции с v16. Проверьте следующий ответ от @ brad-adams
Кунал Парех

2
Ценю упоминание @KunalParekh, но это разные вещи. Мой ответ действителен только в том случае, если html находится в вашем приложении (то есть, на самом деле это JSX). Чтобы проанализировать HTML из внешнего источника в jsx, вам нужно искать другое решение.
Брэд Адамс

114

Является this.props.match.descriptionли строка или объект? Если это строка, она должна быть преобразована в HTML. Пример:

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

Результат: http://codepen.io/ilanus/pen/QKgoLA?editors=1011

Однако, если description: <h1 style="color:red;">something</h1>без кавычек ''вы получите:

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

Если это строка и вы не видите никакой разметки HTML, единственная проблема, которую я вижу, это неправильная разметка.

ОБНОВИТЬ

Если вы имеете дело с HTMLEntitles. Вы должны декодировать их перед отправкойdangerouslySetInnerHTML , поэтому они называли это опасно :)

Рабочий пример:

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это строка, а не объект. Что вы имеете в виду с неправильной разметкой? Вы имеете в виду незамкнутые теги? Реакт должен просто сделать это нет?
Серхио Тапиа

Не могли бы вы вставить сюда console.log (this.props.match.description);
Иланус

Один пример:&lt;p&gt;&lt;strong&gt;Our Opportunity:&lt;/strong&gt;&lt;/p&gt;
Серхио Тапиа

В этом случае вам нужно использовать .innerHTML или декодировать HTMLEntities.
Иланус

Возвращать несколько строк или HTML-код с тегами: function htmlDecode (input) {var e = document.createElement ('div'); e.innerHTML = input; var returnString = ''; for (index = 0; index <e.childNodes.length; index ++) {// случай только строки if (e.childNodes [index] .nodeValue) {returnString + = e.childNodes [index] .nodeValue; } // случай HTML if (e.childNodes [index] .outerHTML) {returnString + = e.childNodes [index] .outerHTML; }} return returnString; }
Крис Адамс

59

Я использую 'response-html-parser'

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

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

Источник на npmjs.com

Поднятие комментария @ okram для большей наглядности:

из его описания на github: преобразует строки HTML непосредственно в компоненты React, избегая необходимости опасно использовать SetInnerHTML от npmjs.com. Утилита для преобразования строк HTML в компоненты React. Избегает использования dangerouslySetInnerHTML и преобразует стандартные элементы HTML, атрибуты и встроенные стили в их эквиваленты React.


11
Использует ли эта библиотека "dangerouslySetInnerHTML" в фоновом режиме?
Омар

1
из его описания на github: Converts HTML strings directly into React components avoiding the need to use dangerouslySetInnerHTMLот 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.
окрам

15

Если у вас есть контроль над тем, откуда берется строка, содержащая html (т.е. где-то в вашем приложении), вы можете воспользоваться новым <Fragment>API, выполнив что-то вроде:

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
В вашем примере нет строки, содержащей html. Это либо jsx, либо простая строка.
Мрквон

3
Ну да, технически вы правы @mrkvon, однако, как я уже говорил, это решение действительно только в том случае, если «html» / jsx - это то, что вы можете контролировать. Не для рендеринга некоторых необработанных HTML, предоставляемых через API, например. До FragmentAPI мне всегда было больно, что требовало дополнительных spanобертываний, которые иногда мешали бы гибким макетам. Когда я наткнулся на этот вопрос в поисках возможного решения, я подумал, что поделюсь тем, как мне это удалось.
Брэд Адамс

2
Спасибо! Это было единственное решение, которое сработало в моем случае. Также, отвечая на комментарий mrkvon к этому ответу: Этот ответ действительно содержит html, т.е. Some text <strong>wrapped with strong</strong>содержит html tag strong.
Бинита Бхарати

@BinitaBharati Но это не строка, хотя. Если вы получаете строку из API, такую ​​как «<p> This is String </ p>» (или просто сохраняете строку в переменной), когда вы помещаете эту строку в <Fragment>, вывод будет по-прежнему содержать < тег p>
Muchdecal

1
@BradAdams. Хороший трюк, хотя. Я могу видеть случаи, когда это становится удобным.
Muchdecal


6

dangerouslySetInnerHTML

dangerouslySetInnerHTML - это замена React для использования innerHTML в DOM браузера. В общем, настройка HTML из кода рискованна, потому что легко непреднамеренно подвергнуть ваших пользователей атаке межсайтового скриптинга (XSS). Таким образом, вы можете установить HTML непосредственно из React, но вы должны ввести опасно SetInnerHTML и передать объект с ключом __html, чтобы напомнить себе, что это опасно. Например:

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

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

3

Я использую innerHTML вместе ref для span:

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

Мне это нравится, нет необходимости в дополнительных библиотеках или опоре на стороне сервера, когда у вас нет такой роскоши. Вдохновленный вами, но в компоненте класса, который я сделал, componentDidMount() { this.message.current.innerHTML = this.state.selectedMessage.body; }body является для меня экранированным html.
webhound

2

В моем случае я использовал response-render-html

Сначала установите пакет npm i --save react-render-html

затем,

import renderHTML from 'react-render-html';

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

1

Я не мог заставить npm buildработать react-html-parser. Однако в моем случае я смог успешно использовать https://reactjs.org/docs/fragments.html . У меня было требование показать несколько html символов Юникода, но они не должны быть непосредственно встроены в JSX. В JSX его нужно было выбирать из состояния компонента. Фрагмент кода компонента приведен ниже:

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

Если у вас есть контроль над {this.props.match.description} и вы используете JSX. Я бы порекомендовал не использовать "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}
);
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.