简体   繁体   中英

Porting nl2br to Javascript/React - How to manually escape text?

I'm porting a small side project from Flask + server-side template rendering to Flask + React. One component uses a Jinja2 custom filter, nl2br, that simply translates newlines in plaintext to <p> and <br> tags in HTML. I'd like to implement something similar in idiomatic Javascript (with a focus on React).

I think the key step I'm missing is a way to manually perform the same escaping that React applies to string variables in JSX. Is that exposed anywhere?

The original Python code:

import re
from jinja2 import evalcontextfilter
from markupsafe import Markup, escape

_paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}')

@evalcontextfilter
def nl2br(eval_ctx, value):
    result = u'\n\n'.join(u'<p>%s</p>' % p.replace('\n', Markup('<br>\n'))
                          for p in _paragraph_re.split(escape(value)))
    if eval_ctx.autoescape:
        result = Markup(result)
    return result

Outline of the code: 1. escape the input, 2. split it into paragraphs ( <p> ) based on pairs of newlines, 3. replace newlines inside paragraphs with <br> tags, and 4. wrap with Markup if configuration demands HTML output and use the @evalcontextfilter decorator to disable unwanted autoescaping beyond that.

The middle two steps can be translated directly to Javascript (just regexp and string processing). I think the React analogue of the last step is to use dangerouslySetInnerHTML . But I can't figure out a good way to perform the first. There are all sorts of HTML escaping libraries on NPM but I'd like to use React's built-in escaping for consistency if possible.

This can be implemented in a slightly different way in React by just escaping the individual strings after building the DOM tree instead of escaping everything before splitting as in the Python code:

const _p_re = /((?:\r\n|\n){2,})/;
const _br_re = /(\r\n|\r|\n)/;

function nl2br(text) {
  if (text === "") return <p />;
  let ps = text.split(_p_re);
  for (let i = 0; i < ps.length; i += 2) {
    const p = ps[i];
    let parts = p.split(_br_re);
    for (let j = 0; j < parts.length; j += 2) {
      parts[j] = <React.Fragment key={j}>{parts[j]}</React.Fragment>
    }
    for (let j = 1; j < parts.length; j += 2) {
      parts[j] = (
        <React.Fragment key={j}>
          <br />
          {parts[j]}
        </React.Fragment>
      );
    }
    ps[i] = <p key={i}>{parts}</p>;
  }
  return <>{ps}</>;
}

NB: I don't think this code uses the key prop correctly, as I don't quite understand that at the moment. But it works for my purposes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM