React: 如何用组件替换字符串的一部分?

创建于 2015-03-12  ·  40评论  ·  资料来源: facebook/react

我有这段代码显然不起作用:

    _.each(matches, (match) ->
      string = string.replace(match, ->
        <span className="match" key={i++}>{match}</span>
      )
    )

因为这会导致字符串与对象混合。 不好我知道。 但是如何在字符串中添加 React 组件呢? 我想要的只是用反应组件突出显示字符串的一部分。 我猜这是一个很难破解的案例。

最有用的评论

您可以将split函数与正则表达式一起使用,然后替换捕获的部分,如下所示:

var parts = "I am a cow; cows say moo. MOOOOO.".split(/(\bmoo+\b)/gi);
for (var i = 1; i < parts.length; i += 2) {
  parts[i] = <span className="match" key={i}>{parts[i]}</span>;
}
return <div>{parts}</div>;

如果这不清楚,请告诉我。

所有40条评论

这样的事情应该工作:

const escapeRE = new RegExp(/([.*+?^=!:$(){}|[\]\/\\])/g)
const safeRE = (string) => {
  return string.replace(escapeRE, "\\$1")
}

class Hightlight extends Component {
  static propTypes = {
    match : PropTypes.string,
    string : PropTypes.string,
  }

  render() {
    return (
      <span
        dangerouslySetInnerHTML={{
          __html : string.replace(safeRE(this.props.match), "<strong className\"match\">$1</strong>")
        }} />
    )
  }
}

是的,我一直在考虑滥用dangerouslySetInnerHTML但我不喜欢这个想法。 这种方法有充分的理由被标记为危险。

有没有办法教 React 解析字符串中的 jsx? 让 React 解析 iE

var example = "this one word <span className="match" key={i++}>hello</span> is highlighted"

并在渲染方法中返回它?

@binarykitchen您可以动态构造 React 元素,这就是您在此处需要做的,仅用字符串替换是无法做到的。

@syranide是的,我已经知道了。 我的观点是:这不是一个很好的 JSX/React 特性吗? 解析一个字符串并将其中的任何标签转换为子 React 组件。

您可以将split函数与正则表达式一起使用,然后替换捕获的部分,如下所示:

var parts = "I am a cow; cows say moo. MOOOOO.".split(/(\bmoo+\b)/gi);
for (var i = 1; i < parts.length; i += 2) {
  parts[i] = <span className="match" key={i}>{parts[i]}</span>;
}
return <div>{parts}</div>;

如果这不清楚,请告诉我。

在运行时解析 JSX 容易出错且速度缓慢,并且很容易产生安全隐患——它本质上不会比危险的 SetInnerHTML 更安全。

这可能是类型案例的一个问题

— 毫升

在12个火星2015年,在21:37,本·阿尔珀特[email protected]写道:

您可以使用带有正则表达式的 split 函数,然后替换捕获的部分,如下所示:

var parts = "我是一头牛;牛说哞哞。MOOOOO.".split(/(\bmoo+\b)/gi);
for (var i = 1; i <parts.length; i += 2) {
零件[i] = ;}返回

{部分}
;
如果这不清楚,请告诉我。


直接回复此邮件或在 GitHub 上查看。

我在尝试以编程方式突出显示部分字符串时也遇到了这个问题(即用<span>标签替换)。 我最终根据上面的@spicyj的解决方案创建了一个模块。 对于任何感兴趣的人: iansinnott/react-string-replace

@iansinnott感谢分享。 我开始使用它然后意识到我将需要完整的String.prototype.replace API(需要访问各个匹配组以获取替换函数中的正则表达式)。 我意识到更改不仅仅是一个简单的补丁(并且需要更改 API),因此我创建了一个新的存储库: https :

如果react-string-replace不符合您的要求:

https://github.com/EfogDev/react-process-string

以下内容对我有用,可以突出显示文本字符串中的关键字,而无需
危险地设置 HTML:

/**
 * Find and highlight relevant keywords within a block of text
 * <strong i="7">@param</strong>  {string} label - The text to parse
 * <strong i="8">@param</strong>  {string} value - The search keyword to highlight
 * <strong i="9">@return</strong> {object} A JSX object containing an array of alternating strings and JSX
 */
const formatLabel = (label, value) => {
  if (!value) {
    return label;
  }
  return (<span>
    { label.split(value)
      .reduce((prev, current, i) => {
        if (!i) {
          return [current];
        }
        return prev.concat(<b key={value + current}>{ value }</b>, current);
      }, [])
    }
  </span>);
};

formatLabel('Lorem ipsum dolor sit amet', 'dolor');
// <span>Lorem ipsum <b>dolor</b> sit amet</span>

我需要这样的东西来匹配某个模式的提及,以便它可以包装多个值并对@richardwestenra解决方案进行一些更改。 也许它对某人有用。

/**
 * Find and highlight mention given a matching pattern within a block of text
 * <strong i="7">@param</strong> {string} text - The text to parse
 * <strong i="8">@param</strong> {array} values - Values to highlight
 * <strong i="9">@param</strong> {RegExp} regex - The search pattern to highlight 
 * <strong i="10">@return</strong> {object} A JSX object containing an array of alternating strings and JSX
 */
const formatMentionText = (text, values, regex) => { 
    if (!values.length)
        return text;

    return (<div>
        {text.split(regex)
            .reduce((prev, current, i) => {
                if (!i)
                    return [current];

                return prev.concat(
                    values.includes(current)  ?
                        <mark key={i + current}>
                            {current}
                        </mark>
                        : current
                );
            }, [])}
    </div>);
};

const text = 'Lorem ipsum dolor sit amet [[Jonh Doe]] and [[Jane Doe]]';
const values = ['Jonh Doe', 'Jane Doe'];
const reg = new RegExp(/\[\[(.*?)\]\]/); // Match text inside two square brackets

formatMentionText(text, values, reg);
// Lorem ipsum dolor sit amet <mark>Jonh Doe</mark> and <mark>Jane Doe</mark>

我有一个类似的问题,我用反应门户解决了它,为了轻松找到节点,我用 div 替换了字符串,并使用 createPortal 将我的反应组件渲染到该 div 中。

@rmtngh我很好奇你为什么需要这样做。 看起来它可能有点矫枉过正。 你在交错非反应代码吗?

关于这个问题的解决方案,我想再次推荐string-replace-to-array (我是作者)。 它已经在多个环境中进行了战斗和时间测试。 它也很小(整个库就是这个文件 https://github.com/oztune/string-replace-to-array/blob/master/string-replace-to-array.js)。

这是正则表达式的使用示例:

import replace from 'string-replace-to-array'

// The API is designed to match the native 'String.replace',
// except it can handle non-string replacements.
replace(
  'Hello Hermione Granger...',
  /(Hermione) (Granger)/g,
  function (fullName, firstName, lastName, offset, string) {
    return <Person firstName={ firstName } lastName={ lastName } key={ offset } />
  }
)

// output: ['Hello ', <Person firstName="Hermione" lastName="Granger" key={ 0 } />, ...]

@oztune感谢您的回复,在这种情况下,我有一个来自 CMS 所见即所得编辑器的 html 标记字符串,我想在该标记内的某些位置放置反应组件。
您认为使用库而不是使用门户会更好吗? 我认为门户所做的只是在另一个节点内渲染组件而不是最近的节点,这不会比渲染普通组件更昂贵,唯一的额外步骤是替换可以用原生 js 替换完成的字符串。
我错过了什么吗? 改用图书馆会有什么好处吗?

@rmtngh我想说这取决于你想要做什么。 如果你只是想在你的 DOM 树的不同区域散布一些 React 组件,并且树还没有被 React 渲染,那么门户就是你要走的路。 当您尝试替换的部分已经在 React 组件中呈现时,我提到的方法最有用。

这是返回给定文本和模式的节点数组的代码:

const highlightPattern = (text, pattern) => {
  const splitText = text.split(pattern);

  if (splitText.length <= 1) {
    return text;
  }

  const matches = text.match(pattern);

  return splitText.reduce((arr, element, index) => (matches[index] ? [
    ...arr,
    element,
    <mark>
      {matches[index]}
    </mark>,
  ] : [...arr, element]), []);
};

谢谢@wojtekmaj
我的模式组版本:

const replacePatternToComponent = (text, pattern, Component) => {
  const splitText = text.split(pattern);
  const matches = text.match(pattern);

  if (splitText.length <= 1) {
    return text;
  }

  return splitText.reduce((arr, element) => {
      if (!element) return arr;

      if(matches.includes(element)) {
        return [...arr, Component];
      }

      return [...arr, element];
    },
    []
  );
};

const string = 'Foo [first] Bar [second]';
const pattern = /(\[first\])|(\[second\])/g;

replacePatternToComponent(string, pattern, <Component />);

使用打字稿的方法:

const formatString = (
  str: string,
  formatingFunction?: (value: number | string) => React.ReactNode,
  ...values: Array<number | string>
) => {
  const templateSplit = new RegExp(/{(\d)}/g);
  const isNumber = new RegExp(/^\d+$/);
  const splitedText = str.split(templateSplit);
  return splitedText.map(sentence => {
    if (isNumber.test(sentence)) {
      const value = values[Number(sentence)];
      return Boolean(formatingFunction) ? formatingFunction(value) : value;
    }
    return sentence;
  });
};

使用示例:

const str = '{0} test {1} test';
formatString(str, value => <b>{value}</b>, value1, value2);

结果:
<b>value1</b> test <b>value2</b> test

@rmtngh您能否提供一个示例,说明您如何通过 createportal 实现标记丰富? 我也在做同样的事情。

@Dashue这个想法是在你的标记中为你的反应组件创建一些目标,这取决于 HTML 字符串以及你对它的控制程度,
如果您可以在服务器端更改它,您可能需要为您的元素提供一个 ID。 就像是 :
<div id="component_target_1"></div>
在 react 中,使用正则表达式查找\id="(component_target_\d)"\g ,存储 ids (state.targets) 并渲染您的组件:

let mappedComponents
if(this.state.targets && this.state.targets.length){
            mappedComponents = this.state.targets.map((id,index)=><MyComponent id={id} key={id} />)
        }

这是“MyComponent”的示例:

import React from 'react'
import ReactDOM from 'react-dom'

export default class MyComponent extends React.Component {
  constructor(props) {
    super(props)
  }
  getWrapper(){
    return document.getElementById(this.props.id)
  }
  render() {
    if(!this.getWrapper()) return null

    const content = <div>
        Hello there! I'm rendered with react.
      </div>

      return ReactDOM.createPortal(
        content,
        this.getWrapper(),
      );
  }
}

如果您对 HTML 字符串没有太多控制权,您仍然可以使用相同的方法,您可能需要找到一些元素并将目标元素注入字符串。

伟大的线程家伙!

我创建了一个简单的React Text Highlighter笔,它实现了这个线程中的一些想法(还有一些额外的魔法......),希望未来的访问者会发现它很有用。

这是使用map的解决方案,
在此处分叉@sophiebits解决方案:

const reg = new RegExp(/(\bmoo+\b)/, 'gi');
const parts = text.split(reg);
return <div>{parts.map(part => (part.match(reg) ? <b>{part}</b> : part))}</div>;

美丽的解决方案@rehat101

这是在我的情况下最有效的方法 - 按空格字符拆分并查找特定单词。

HTML 中有点乱,但谁在乎 😂

💗 RegExpmap使这一切变得如此干净。 💗💗💗

image

const Speech = ({ speech, speeches, setSpeeches, i, text }) => {

const highlightRegExp = new RegExp(
    /you|can|do|it|puedes|hacerlo|pingüinos|son|bellos/,
    "gi"
  );
  const delineator = " ";
  const parts = text.split(delineator);

  return (
        <div>
          {parts.map(part =>
            part.match(highlightRegExp) ? <b>{part + " "}</b> : part + " "
          )}
        </div>
  );
};

美丽的@rehat101

如果react-string-replace不符合您的要求:

https://github.com/EfogDev/react-process-string

杰出的! 我发现它非常有用,因为大多数时候您实际上需要访问原始字符串

我稍微重构了@nullhook的解决方案,使其接受来自查询的变量并模拟 Google 搜索自动完成的行为
js const highlightQueryText = (text: string, filterValue: string) => { const reg = new RegExp(`(${filterValue})`, 'gi'); const textParts = text.split(reg); return ( <span style={{ fontWeight: 600 }}> {textParts.map(part => (part.match(reg) ? <span style={{ fontWeight: 'normal' }}>{part}</span> : part))} </span> ); };

所以我发现了一个很好的组合实现 DOMPurify 来清理带有 html-react-parser 的 html 字符串,以在其中找到目标标记并将其转换为 JSX 组件 - 我使用带有传递道具的 Rebass 作为示例......结果非常好,两个包将帮助你避免滚动你自己的解析器的开销,我只是不推荐这个没有清理的解析器包。 对于那些为此苦苦挣扎的人,仍然可以考虑一下这个代码笔的价值: https ://codesandbox.io/s/eager-wiles-5hpff

嘿杰西,
这似乎是一个非常好的组合,用于获取文本并将其转换为
反应组件! 更换零件肯定会很好
带组件的字符串。

感谢分享。

在 JAMstack 站点中将 Markdown 转换为组件也很有用。

最好的祝福,
德里克

Derek R. Austin,PT,DPT,MS,BCTMB,LMT,CSCS

在 LinkedIn 上加入我: https :

2020 年 2 月 26 日,星期三,下午 4:35,Jesse Lewis [email protected]写道:

所以我找到了一个很好的组合来实现 DOMPurify 来清理 html
带有 html-react-parser 的字符串以在其中查找目标标记并进行转换
到 JSX 组件 - 我使用带有传递道具的 Rebass 作为示例..
出来的很好,这两个包将帮助您避免开销
滚动你自己的解析器,我只是不推荐这个解析器包
没有消毒。 对于那些为此苦苦挣扎的人来说,仍然可能有范围
这个代码笔的价值:
https://codesandbox.io/s/eager-wiles-5hpff。 对任何改进的反馈
也非常感谢。


您收到此消息是因为您发表了评论。
直接回复本邮件,在GitHub上查看
https://github.com/facebook/react/issues/3386?email_source=notifications&email_token=AMV42QTV4FDXZIFXCGB3NZDRE3OATA5CNFSM4A5VRBI2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJLQ9TDN5LMVXHJQ19000000000000000000000785600004
或取消订阅
https://github.com/notifications/unsubscribe-auth/AMV42QXARIOKOYSJ4C5IFQLRE3OATANCNFSM4A5VRBIQ
.

您可以将split函数与正则表达式一起使用,然后替换捕获的部分,如下所示:

var parts = "I am a cow; cows say moo. MOOOOO.".split(/(\bmoo+\b)/gi);
for (var i = 1; i < parts.length; i += 2) {
  parts[i] = <span className="match" key={i}>{parts[i]}</span>;
}
return <div>{parts}</div>;

如果这不清楚,请告诉我。

这很好用。 有什么方法可以让它在 Angular 中工作? 我已经发布了一个关于它的问题,使用您的代码:
https://stackoverflow.com/questions/60889171/wrap-certain-words-with-a-component-in-a-contenteditable-div-in-angular

你好朋友,我需要你的帮助,我正在使用 React Native,我需要做的是从我从数据库中提取的文本中应用格式(字体颜色、链接)来制作@mentions ,如果在text 找到 1 个单一匹配,使得替换一切顺利,但是! 如果文本中有多个@mentions ,则会引发错误。

/////文本示例: _hey发生了什么@-id:1-一切正常??
////列出用户它的数组,例如:[idusuario: "1", usuario: "@luigbren", format: "@-id:1-".....]

const PatternToComponent = (text, usuarios,) => {
    let mentionsRegex = new RegExp(/@-(id:[0-9]+)-/, 'gim');
    let matches = text.match(mentionsRegex);
    if (matches && matches.length) {
        matches = matches.map(function (match, idx) {
            let usrTofind = matches[idx]
            //////////////////////////////////////////////////////////////////
            const mentFormat = listusers.filter(function (item) { 
                const itemData = item.format;
                return itemData.indexOf(usrTofind) > -1;
            });
            if (mentFormat.length > 0) {
                let idusuario = mentFormat[0].idusuario
                let replc = mentFormat[0].usuario

                console.log(usrTofind) //// here find @-id:1-
                console.log(replc)   //// here is <strong i="12">@luigbren</strong> for replace

                ////////// now here replace part of the string, @-id:1- with a <Text> component 
                ///////// with <strong i="13">@luigbren</strong> and the link, this is repeated for every <strong i="14">@mention</strong> found

                parts = text.split(usrTofind);
                for (var i = 1; i < parts.length; i += 2) {
                  parts[i] = <Text key={i} style={{ color: '#00F' }} onPress={() => { alert('but this is'); }}>{replc}</Text>;
                }
                return text = parts;
                /////////////////////////////////////////////////////////////////////
            } else {
                return text
            }
        });
    } else {
        return text
    }
    return text
};

以这种方式,只有在文本中只有一个提及时,代码才对我有效,例如 '_hey 发生了什么 @-id:1- 一切正常 ??_' ,但是当放置不止一个提及时,它会给我一个错误,例如:'_hey发生了什么@-id:1-一切正常?? @-id:2- @-id:3-_' ... 错误: TypeError: text.split 不是函数,如果不是放置 _parts = text.split(usrTofind);_ 我放置 _parts = text.toString ().split(usrTofind);_ 它给了我一个 [Object Object] 错误

您可以使用html-react-parser来解析字符串,然后将节点名称替换为 jsx 组件。 这种类型的替换将允许您通过替换字符串动态地使用 react 元素。

      parse(body, {
        replace: domNode => {
          if (domNode.name === 'select') { // or
            return React.createElement(
              Select, // your react component
              { },
              domToReact(domNode.children)
            );
          }
        }

有人告诉我如何替换字符串中的react-router-dom

例子:

var text = "Are You okay <strong i="9">@sara</strong> ?";

var href= <Link to={{
  pathname: '/user/sara',
}}> <strong i="10">@sara</strong> </Link>;

var replace = text.replace("@sara", href);

//output : Are You okey [Object Object] ?

我真的不知道 [Object Object] 是谁? 我说“你还好吗@sara”,但这段代码调用了其他人!

我在react-native有几乎相同的问题,如果有人可以帮助我,我将不胜感激

我的问题

除了这个包https://www.npmjs.com/package/regexify-string对我没有任何帮助

我正在使用这个功能:

export const replaceWithHtml = (
  text: string,
  textToReplace: string,
  replaceValue: any,
): any[] => {
  const delimiter = '|||||'
  return text && text.includes(textToReplace)
    ? text
        .replace(textToReplace, `${delimiter}${textToReplace}${delimiter}`)
        .split(delimiter)
        .map((i) => {
          if (i === textToReplace) {
            return replaceValue
          } else {
            return i || null
          }
        })
    : text
}

像那样:

const text = 'This is an [icon]'
replaceWithHtml(
      text,
      '[icon]',
      <i className="icon-cogs" />,
)

我只是想要一个简单的插值工具,用某些元素替换某些关键字。

为了减少输出中的元素数量,我使用了React.Fragment

const interpolate = (text, values) => {
  const pattern = /([$0-9]+)/g
  const matches = text.match(pattern)
  const parts = text.split(pattern)

  if (!matches) {
    return text
  }

  return parts.map((part, index) => (
    <Fragment key={part + index}>{matches.includes(part) ? values[part] : part}</Fragment>
  ))
}

// ...

<p>
  {interpolate('$1 with $2 is fun!', {
    $1: <em>Playing</em>,
    $2: <strong>JavaScript</strong>,
  })}
</p>

// <p><em>Playing</em> with <strong>JavaScript</strong> is fun!</p>

感谢@sophiebits关于split()想法🙏

基于@pedrodurek的解决方案,这里有一些可以满足我特定需求的内容(使用可读键进行翻译,而不仅仅是数字):

export const insertComponentsIntoText = (
    str: string,
    replacements: {
        [key: string]: React.ReactNode
    }
) => {
    const splitRegex = new RegExp(/\[\[(\w*)\]\]/g);
    const parts = str.split(splitRegex);
    return parts.map(part => {
        if (replacements.hasOwnProperty(part)) {
            return replacements[part];
        }
        return part;
    });
};

例子:

insertComponentsIntoText(`"Please accept our [[privacyPolicyLink]] and [[termsLink].", {
    "privacyPolicyLink": <a key="privacyPolicyLink" href="/privacy">Privacy Policy</a>,
    "termsLink": <a key="termsLink" href="/terms">Terms and Conditions</a>
})

……变成……

Please accept our <a key="privacyPolicyLink" href="/privacy">Privacy Policy</a> and <a key="termsLink" href="/terms">Terms and Conditions</a>.

(键是必需的,因为它实际上变成了一个数组,而 react 不喜欢没有键的数组元素。)

除了这个包https://www.npmjs.com/package/regexify-string对我没有任何帮助

我也是。 尝试了所有这些,只有这个包有效。 谢谢!

此页面是否有帮助?
0 / 5 - 0 等级