2015-11-16 2 views
1

Я показываю текст, который был сохранен в базе данных. Данные поступают из firebase в виде строки (с включенными перерывами в новой строке). Чтобы он отображался как HTML, я изначально сделал следующее:Как объединить компонент JSX с опасностьюSetInnerHTML

<p className="term-definition" 
     dangerouslySetInnerHTML={{__html: (definition.definition) ? definition.definition.replace(/(?:\r\n|\r|\n)/g, '<br />') : ''}}></p> 

Это отлично работало. Однако есть еще одна особенность. Пользователи могут ввести [word], и это слово будет связано. Для того, чтобы достичь этого, я создал следующую функцию:

parseDefinitionText(text){ 
    text = text.replace(/(?:\r\n|\r|\n)/g, '<br />'); 
    text = text.replace(/\[([A-Za-z0-9'\-\s]+)\]/, function(match, word){ 
     // Convert it to a permalink 
     return (<Link to={'/terms/' + this.permalink(word) + '/1'}>{word}</Link>); 
    }.bind(this)); 
    return text; 
    }, 

я ушел из метода this.permalink, как это не имеет значения. Как вы можете видеть, я пытаюсь вернуть компонент <Link>, который был импортирован из response-router.However, поскольку это необработанный HTML, dangerouslySetInnerHTML больше не работает должным образом.

Так что я как бы застрял в этой точке. Что я могу сделать для форматирования внутреннего текста и создания ссылки?

ответ

2

Вы можете разделить текст в массив ссылок + строк так:

import {Link} from 'react-router'; 

const paragraphWithLinks = ({markdown}) => { 
    const linkRegex = /\[([\w\s-']+)\]/g; 

    const children = _.chain(
    markdown.split(linkRegex) // get the text between links 
).zip(
    markdown.match(linkRegex).map(// get the links 
     word => <Link to={`/terms/${permalink(word)}/1`}>{word}</Link> // and convert them 
    ) 
).flatten().thru(// merge them 
    v => v.slice(0, -1) // remove the last element (undefined b/c arrays are different sizes) 
).value(); 

    return <p className='term-definition'>{children}</p>; 
}; 

Лучше всего об этом подходе устраняет необходимость использования dangerouslySetInnerHTML. Обычно это крайне плохой идеей, поскольку вы потенциально создаете уязвимость XSS. Это может позволить хакерам, например, украсть учетные данные для входа у ваших пользователей.

В большинстве случаев вам не нужно использовать dangerouslySetHTML. Очевидным исключением является интеграция с третьей стороной библиотеки, которую все равно следует тщательно рассмотреть.

+0

Что такое _.chain? Вы используете underscorejs? У меня нет этого как часть этого проекта, и я не хочу добавлять дополнительные зависимости. –

+0

Это от 'lodash'. Очень похоже на подчеркивание, и довольно здорово. Я дал вам суть того, как вы могли бы создать парсер разметки - реализация зависит от вас. –

+0

'_.chain' позволяет выполнять функции последовательно, подавая вход от одной функции к следующей. –

0

Я столкнулся с подобной ситуацией, однако принятое решение не было жизнеспособным вариантом для меня.

Я получил эту работу с react-dom довольно грубо. Я установил компонент для прослушивания событий щелчка, и если бы щелчок имел класс react-router-link. Когда это произошло, если в элементе есть свойство data-url, оно использует browserHistory.push. В настоящее время я использую изоморфное приложение, и эти события кликов не имеют смысла для генерации сервера, поэтому я устанавливаю только эти события условно.

Вот код, который я использовал:

import React from 'react'; 
import _ from 'lodash'; 
import { browserHistory } from 'react-router' 

export default class PostBody extends React.Component { 
    componentDidMount() { 
    if(! global.__SERVER__) { 
     this.listener = this.handleClick.bind(this); 
     window.addEventListener('click', this.listener); 
    } 
    } 

    componentDidUnmount() { 
    if(! global.__SERVER__) { 
     window.removeEventListener("scroll", this.listener); 
    } 
    } 

    handleClick(e) { 
    if(_.includes(e.target.classList, "react-router-link")) { 
     window.removeEventListener("click", this.listener); 
     browserHistory.push(e.target.getAttribute("data-url")); 
    } 
    } 

    render() { 
    function createMarkup(html) { return {__html: html}; }; 

    return (
     <div className="col-xs-10 col-xs-offset-1 col-md-6 col-md-offset-3 col-lg-8 col-lg-offset-2 post-body"> 
     <div dangerouslySetInnerHTML={createMarkup(this.props.postBody)} /> 
     </div> 
    ); 
    } 

} 

Надеется, что это выручает!

Смежные вопросы