用React方式在HTML中包装多个字符串 [英] Wrap multiple strings in HTML the React way

查看:109
本文介绍了用React方式在HTML中包装多个字符串的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在构建一个实体荧光笔,因此我可以上传文本文件,查看屏幕上的内容,然后突出显示数组中的单词。用户在手动突出显示选项时会填充此数组,例如..

I'm building an entity highlighter so I can upload a text file, view the contents on the screen, then highlight words that are in an array. This is array is populated by the user when they manually highlight a selection e.g...

const entities = ['John Smith','Apple' ,'其他一些词'];

这是我的文本文档,显示在屏幕上。它包含大量文本,一旦用户手动突出显示某些文本,一些文本需要在视觉上突出显示,例如John Smith,Apple等名称

现在我希望通过将其包装在某个标记中来直观地突出显示文本中实体的所有实例,并且做这样的操作非常有效:

Now I want to visually highlight all instances of the entity in the text by wrapping it in some markup, and doing something like this works perfectly:

getFormattedText() {
    const paragraphs = this.props.text.split(/\n/);
    const { entities } = this.props;

    return paragraphs.map((p) => {
        let entityWrapped = p;

        entities.forEach((text) => {
        const re = new RegExp(`${text}`, 'g');
        entityWrapped =
            entityWrapped.replace(re, `<em>${text}</em>`);
        });

        return `<p>${entityWrapped}</p>`;
    }).toString().replace(/<\/p>,/g, '</p>');
}

...但是(!),这只是给了我一个大字符串所以我必须危险地设置内部HTML,因此我不能在任何这些突出显示的实体上附加onClick事件React方式,这是我需要做的事情。

...however(!), this just gives me a big string so I have to dangerously set the inner HTML, and therefor I can't then attach an onClick event 'the React way' on any of these highlighted entities, which is something I need to do.

这样做的React方法是返回一个如下所示的数组:

The React way of doing this would be to return an array that looks something like this:

['这是我的文字屏幕上显示的文档。它包含大量文本,其中一些文本需要在视觉上突出显示给用户,例如名称',{},{},{}] 其中 {} 是包含JSX内容的React对象。

['This is my text document that is displayed on the screen. It contains a lot of text, and some of this text needs to be visually highlighted to the user, like the name', {}, {}, {}] Where the {} are the React Objects containing the JSX stuff.

我已经用一些嵌套循环对此进行了攻击,但它是有缺陷的因为地狱,难以阅读,随着我逐渐增加更多的实体,性能受到了巨大的打击。

I've had a stab at this with a few nested loops, but it's buggy as hell, difficult to read and as I'm incrementally adding more entities the performance takes a huge hit.

所以,我的问题是......什么是最好的方法解决这个问题?确保代码简单易读,并且我们不会遇到巨大的性能问题,因为我可能会处理很长的文档。这是我放弃我的React道德和危险的SetInnerHTML以及直接绑定到DOM的事件的时间吗?

So, my question is... what's the best way to solve this issue? Ensuring code is simple and readable, and that we don't get huge performance issues, as I'm potentially dealing with documents which are very long. Is this the time that I let go of my React morals and dangerouslySetInnerHTML, along with events bound directly to the DOM?

@ AndriciCezar下面的答案可以很好地格式化为React渲染的字符串和对象数组,但是一旦实体数组很大(> 100)并且文本正文也是如此,它就不是很有效大(> 100kb)。我们正在寻找大约10倍的时间来渲染它作为一个数组V是一个字符串。

@AndriciCezar's answer below does a perfect job of formatting the array of Strings and Objects ready for React to render, however it's not very performant once the array of entities is large (>100) and the body of text is also large (>100kb). We're looking at about 10x longer to render this as an array V's a string.

有没有人知道更好的方法来做这个,提供渲染的速度大字符串,但能够在元素上附加React事件的灵活性?或者危险的SetInnerHTML是这种情况下的最佳解决方案吗?

Does anyone know a better way to do this that gives the speed of rendering a large string but the flexibility of being able to attach React events on the elements? Or is dangerouslySetInnerHTML the best solution in this scenario?

推荐答案

这是一个使用正则表达式在每个关键字上拆分字符串的解决方案。如果您不需要它不区分大小写或突出显示多个单词的关键字,您可以使这更简单。

Here's a solution that uses a regex to split the string on each keyword. You could make this simpler if you don't need it to be case insensitive or highlight keywords that are multiple words.

import React from 'react';

const input = 'This is a test. And this is another test.';
const keywords = ['this', 'another test'];

export default class Highlighter extends React.PureComponent {
    highlight(input, regexes) {
        if (!regexes.length) {
            return input;
        }
        let split = input.split(regexes[0]);
        // Only needed if matches are case insensitive and we need to preserve the
        // case of the original match
        let replacements = input.match(regexes[0]);
        let result = [];
        for (let i = 0; i < split.length - 1; i++) {
            result.push(this.highlight(split[i], regexes.slice(1)));
            result.push(<em>{replacements[i]}</em>);
        }
        result.push(this.highlight(split[split.length - 1], regexes.slice(1)));
        return result;
    }
    render() {
        let regexes = keywords.map(word => new RegExp(`\\b${word}\\b`, 'ig'));
        return (
            <div>
                { this.highlight(input, regexes) }
            </div>);
    }
}

这篇关于用React方式在HTML中包装多个字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆