Ad

React : Rendering A Syntax Highlighted Code Block

- 1 answer

I'm working on an article system using React and JSX. My articles sometimes have code examples within their content. I have implemented highlight.js to add style to these blocks. My main problem is that my articles use HTML tags, I therefore use React's dangerouslySetInnerHTML method. This works alright but of course any HTML code in my code blocks get interpreted as HTML. I was wondering if any of you had any insight on how I should implement this. Should I remove all HTML from my content and parse it (using markdown) before safely rendering it as text ?

Thanks for reading this.

Ad

Answer

My solution was to implement a simple "markdown" like parser within my article view. The idea is to parse the markdown using regex and return JSX elements from those results. I'm not so good at regular expressions (this could probably be improved) but here goes :

module.exports = React.createClass({
//this regex matchs \n\n, all wrapping ** **, wrapping ''' '''
mainRegex: /(\n\n|\*\*(?:[\s\S]*?)\*\*|'''(?:[\s\S]*?)''')/g,
titleRegex : /(?:\*\*([\s\S]*?)\*\*)/,
codeRegex : /(?:'''([\s\S]*?)''')/,

getInitialState: function() {
    return {
        content: []
    };
},

setContent: function() {
    if (this.props.htmlContent && !this.state.content.length) this.setState( {content: this.formatText(this.props.htmlContent) });
},

formatText: function(_text) {

    var _this = this;
    var _content = [];

    _text = _text.split(this.mainRegex);

    _text.forEach(function(_frag) {

        if (!/\n\n/g.test(_frag) ) {

            if (_this.titleRegex.test(_frag))  _content.push( {type: "h2", value: _frag.match(_this.titleRegex)[1] } );
            else if (_frag!=="") _content.push({type: "p", value: _frag});

        } else if (_this.codeRegex.test(_frag))  {

            _content.push( {type: "code", value: _frag.match(_this.codeRegex)[1] } );
        }

    });

    return _content;
},
render: function() {

    return <article>
        {this.state.content.map(this.renderText)}
    </article>;

},

renderText:function(_tag, i) {

    switch (_tag.type) {

        case "p":
            return <p key={i}>{_tag.value}</p>;
            break;

        case "h2":
            return <h2 key={i}>{_tag.value}</h2>;
            break;

        case "code":
            return <pre key={i}><code clasName="js">{_tag.value}</code></pre>;
            break;

    }

},

componentDidUpdate: function() {

    this.setContent();
    this.highlightCode();

},

highlightCode: function() {

    var _codeBlocks = document.getElementsByTagName('code');
    for (var i = 0, j = _codeBlocks.length; i<j; ++i) {
        hljs.highlightBlock(_codeBlocks[i]);
    }

}
});
Ad
source: stackoverflow.com
Ad