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 ์„ ๋‚จ์šฉํ•  ์ƒ๊ฐ์„ ํ–ˆ์ง€๋งŒ ์ด ์•„์ด๋””์–ด๊ฐ€ ๋งˆ์Œ์— ๋“ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ๋ฐฉ๋ฒ•์€ ํ•ฉ๋‹นํ•œ ์ด์œ ๋กœ ์œ„ํ—˜ํ•œ ๊ฒƒ์œผ๋กœ ๋ถ„๋ฅ˜๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๋ฌธ์ž์—ด ๋‚ด์—์„œ jsx๋ฅผ ๊ตฌ๋ฌธ ๋ถ„์„ํ•˜๋„๋ก React๋ฅผ ๊ฐ€๋ฅด์น  ๋ฐฉ๋ฒ•์ด ์—†๋‚˜์š”? 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๋ณด๋‹ค ์•ˆ์ „ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ทธ๊ฒƒ์€ ์œ ํ˜• ์ผ€์ด์Šค์— ์•ฝ๊ฐ„์˜ ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

โ€” mlb

2015๋…„ 3์›” 12์ผ 21์‹œ 37๋ถ„์— Ben Alpert [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://github.com/oztune/string-replace-to-array

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์˜ wysiwyg ํŽธ์ง‘๊ธฐ์—์„œ ๊ฐ€์ ธ์˜จ 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 />);

typescript๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ ‘๊ทผ:

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>
๊ทธ๋ฆฌ๊ณ  ๋ฐ˜์‘ ๋‚ด๋ถ€์—์„œ ์ •๊ทœ ํ‘œํ˜„์‹์œผ๋กœ \id="(component_target_\d)"\g ๋ฅผ ์ฐพ๊ณ  ID(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์—์„œ ์กฐ๊ธˆ ์ง€์ €๋ถ„ํ•˜์ง€๋งŒ ๋ˆ„๊ฐ€ ์ƒ๊ด€ ๐Ÿ˜‚

๐Ÿ’— RegExp ์™€ ์ง€๋„ ๋Š” ์ด๊ฒƒ์„ ์•„์ฃผ ๊นจ๋—ํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๐Ÿ’—๐Ÿ’—๐Ÿ’—

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 ๊ตฌ์„ฑ ์š”์†Œ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ข‹์€ ์กฐํ•ฉ์„ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค. ์ „๋‹ฌ๋œ props์™€ ํ•จ๊ป˜ Rebass๋ฅผ ์˜ˆ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ฝค ๋ฉ‹์ง€๊ฒŒ ๋‚˜์™”์Šต๋‹ˆ๋‹ค. ๋‘ ๊ฐœ์˜ ํŒจํ‚ค์ง€๋Š” ์ž์‹ ์˜ ํŒŒ์„œ๋ฅผ ๋กค๋งํ•˜๋Š” ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ํ”ผํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ €๋Š” ์ด ํŒŒ์„œ ํŒจํ‚ค์ง€๋ฅผ ์œ„์ƒ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์œผ๋กœ ์–ด๋ ค์›€์„ ๊ฒช๊ณ  ์žˆ๋Š” ์‚ฌ๋žŒ๋“ค์„ ์œ„ํ•ด https://codesandbox.io/s/eager-wiles-5hpff ์—์„œ ์ด ์ฝ”๋“œํŽœ์˜ ๊ฐ€์น˜๋ฅผ ์‚ดํŽด๋ณด์‹ญ์‹œ์˜ค

ํ—ค์ด ์ œ์‹œ,
ํ…์ŠคํŠธ๋ฅผ ๊ฐ€์ ธ์™€์„œ
๋ฐ˜์‘ ๊ตฌ์„ฑ ์š”์†Œ! ํ™•์‹คํžˆ ๋ถ€ํ’ˆ๊ต์ฒด์šฉ์œผ๋กœ ์ข‹์„๋“ฏ
๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์žˆ๋Š” ๋ฌธ์ž์—ด.

๊ณต์œ ํ•ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

JAMstack ์‚ฌ์ดํŠธ์—์„œ Markdown์„ ๊ตฌ์„ฑ ์š”์†Œ๋กœ ์ „ํ™˜ํ•˜๋Š” ๋ฐ ์œ ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์นœ์• ํ•˜๋Š”,
๋ฐ๋ฆญ

๋ฐ๋ฆญ R. ์˜ค์Šคํ‹ด, PT, DPT, MS, BCTMB, LMT, CSCS

LinkedIn์—์„œ ์ €์™€ ํ•จ๊ป˜ํ•˜์„ธ์š”: https://www.linkedin.com/in/derek-austin/

2020๋…„ 2์›” 26์ผ ์ˆ˜์š”์ผ ์˜คํ›„ 4:35 Jesse Lewis [email protected]์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ผ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ DOMPurify๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ HTML์„ ์‚ญ์ œํ•˜๋Š” ์ข‹์€ ์กฐํ•ฉ์„ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค.
html-react-parser๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋Œ€์ƒ ๋งˆํฌ์—…์„ ์ฐพ๊ณ  ๋ณ€ํ™˜ํ•˜๋Š” ๋ฌธ์ž์—ด
JSX ๊ตฌ์„ฑ ์š”์†Œ์— - ์ „๋‹ฌ๋œ ์†Œํ’ˆ๊ณผ ํ•จ๊ป˜ Rebass๋ฅผ ์˜ˆ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
๊ฝค ๋ฉ‹์ง€๊ฒŒ ๋‚˜์™”๊ณ  ๋‘ ํŒจํ‚ค์ง€๋Š” ์˜ค๋ฒ„ ํ—ค๋“œ๋ฅผ ํ”ผํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.
์ž์‹ ์˜ ํŒŒ์„œ๋ฅผ ๋กค๋งํ•˜๋ ค๋ฉด ์ด ํŒŒ์„œ ํŒจํ‚ค์ง€๋ฅผ ๊ถŒ์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
์‚ด๊ท ํ•˜์ง€ ์•Š๊ณ . ์ด๊ฒƒ์œผ๋กœ ์–ด๋ ค์›€์„ ๊ฒช๊ณ ์žˆ๋Š” ์‚ฌ๋žŒ๋“ค์„ ์œ„ํ•ด ์•„์ง ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด ๋‚ฌ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๊ฐ€์น˜๊ฐ€ ์žˆ๋Š” ์ด codepen:
https://codesandbox.io/s/eager-wiles-5hpff. ๊ฐœ์„  ์‚ฌํ•ญ์— ๋Œ€ํ•œ ํ”ผ๋“œ๋ฐฑ
๋„ˆ๋ฌด ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

โ€”
๋‹น์‹ ์ด ๋Œ“๊ธ€์„ ๋‹ฌ์•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์„ ๋ฐ›๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
์ด ์ด๋ฉ”์ผ์— ์ง์ ‘ ๋‹ต์žฅํ•˜๊ณ  GitHub์—์„œ ํ™•์ธํ•˜์„ธ์š”.
https://github.com/facebook/react/issues/3386?email_source=notifications&email_token=AMV42QTV4FDXZIFXCGB3NZDRE3OATA5CNFSM4A5VRBI2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXH
๋˜๋Š” ๊ตฌ๋… ์ทจ์†Œ
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๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฉฐ DB์—์„œ ์ถ”์ถœํ•œ ํ…์ŠคํŠธ์—์„œ @mentions ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ํ˜•์‹(๊ธ€๊ผด ์ƒ‰์ƒ, ๋งํฌ)์„ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด ํ…์ŠคํŠธ์—์„œ ์ถ”์ถœํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ…์ŠคํŠธ๊ฐ€ 1๊ฐœ์˜ ๋‹จ์ผ ์ผ์น˜ ํ•ญ๋ชฉ์„ ์ฐพ์œผ๋ฉด ๊ต์ฒด๊ฐ€ ๋ชจ๋‘ ์ข‹์ง€๋งŒ! ํ…์ŠคํŠธ์— @๋ฉ˜์…˜ ์ด ์—ฌ๋Ÿฌ ๊ฐœ ์žˆ์œผ๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

/////ํ…์ŠคํŠธ ์˜ˆ: _ํ—ค์ด @-id:1- ๋ชจ๋“  ๊ฒƒ์ด ์ •์ƒ ??
//// listusers ๋ฐฐ์—ด, ์˜ˆ: [idusuario: "1", usuario: "@luigbren", ํ˜•์‹: "@-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 what broken @-id:1- everything ok ??_' , ๊ทธ๋Ÿฌ๋‚˜ ๋‘˜ ์ด์ƒ์˜ ๋ฉ˜์…˜์„ ๋ฐฐ์น˜ํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. , ์˜ˆ: '_์ด๋ด ๋ฌด์Šจ ์ผ์ด ์žˆ์—ˆ์–ด @-id:1- ๋‹ค ๊ดœ์ฐฎ์•„ ?? @-id:2- @-id:3-_' ... ์˜ค๋ฅ˜: TypeError: text.split์€ ํ•จ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ฉฐ if ๋Œ€์‹  _parts = text.split(usrTofind);_ _parts = text.toString์„ ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค. ().split(usrTofind);_ [Object Object] ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

html-react-parser ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฌธ์ž์—ด์„ ๊ตฌ๋ฌธ ๋ถ„์„ํ•œ ๋‹ค์Œ ๋…ธ๋“œ ์ด๋ฆ„์œผ๋กœ jsx ๊ตฌ์„ฑ ์š”์†Œ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์œ ํ˜•์˜ ๋Œ€์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฌธ์ž์—ด์„ ๋Œ€์ฒดํ•˜์—ฌ ๋ฐ˜์‘ ์š”์†Œ๋ฅผ ๋™์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

      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>

split() ์•„์ด๋””์–ด์— ๋Œ€ํ•ด @sophiebits ์—๊ฒŒ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค ๐Ÿ™

@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>.

(์‹ค์ œ๋กœ ๋ฐฐ์—ด์ด ๋˜๊ธฐ ๋•Œ๋ฌธ์— ํ‚ค๊ฐ€ ํ•„์š”ํ•˜๊ณ  ๋ฐ˜์‘์€ ํ‚ค๊ฐ€ ์—†๋Š” ๋ฐฐ์—ด ์š”์†Œ๋ฅผ ์ข‹์•„ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.)

์ด ํŒจํ‚ค์ง€๋ฅผ ์ œ์™ธํ•˜๊ณ ๋Š” ์•„๋ฌด๊ฒƒ๋„ ์ž‘๋™ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. https://www.npmjs.com/package/regexify-string

์ €๋„ ์š”. ์ด ํŒจํ‚ค์ง€๋งŒ ์ž‘๋™ํ•˜๋„๋ก ๋ชจ๋‘ ์‹œ๋„ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰