-
Notifications
You must be signed in to change notification settings - Fork 47.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to replace parts of a string with a component? #3386
Comments
something like this should work : 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>")
}} />
)
}
} |
Yes, I've been thinking of abusing Is there no way to teach React to parse jsx within a string? Have React parse i.E.
and return this in a render method? |
@binarykitchen You can construct React elements dynamically and that's what you need to do here, you just can't do it with only string replacement. |
@syranide Yes, I already know that. My point is: wouldn't this be a nice JSX/React feature? Parse a string and turn any tags inside into child React components. |
You can use the 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>; Let me know if that's unclear. |
Parsing JSX at runtime is error-prone and slow, and can easily have security implications – it wouldn't inherently be any safer than dangerouslySetInnerHTML is. |
that can be a bit of an issue with type case — mlb
|
I ran into this issue as well when trying to highlight parts of a string programatically (i.e. replace with |
@iansinnott Thanks for sharing that. I started using it then realized I'm going to need the full |
If |
The following worked for me as way to highlight keyword(s) within a string of text, without /**
* Find and highlight relevant keywords within a block of text
* @param {string} label - The text to parse
* @param {string} value - The search keyword to highlight
* @return {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> |
I needed something like this for mentions matching some pattern so it can wrap multiple values and did some changes to the @richardwestenra solution. Maybe it will be useful for someone.
|
I had a similar issue and I solved it with react portals, to easily find the node, I replaced the string with a div and rendered my react component using createPortal into that div. |
@rmtngh I'm curious why you'd need to do that. It seems like it might be overkill. Are you interlacing non-react code? Regarding solutions to this problem, I'd like to re-recommend Here's a usage example with regex:
|
@oztune Thanks for your reply, In this case I have a string of html markup coming from a wysiwyg editor of a CMS and I'd like to place react components in certain places inside that markup. |
@rmtngh I'd say it depends on what you're trying to do. If you're just trying to sprinkle some React components in different areas of your DOM tree, and the tree isn't already being rendered with React, portals are the way to go. The method I mentioned is most useful when the parts you're trying to replace are already rendered inside of a React component. |
Here's the code that returns an array of nodes given text and pattern: 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]), []);
}; |
Thank you @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 />); |
Approach using 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;
});
}; Example of use: const str = '{0} test {1} test';
formatString(str, value => <b>{value}</b>, value1, value2); Result: |
@rmtngh could you provide a sample of how you achieved the markup enrichment through createportal? I'm looking to do the same. |
@Dashue The idea is to create some targets for your react components inside your markup, depending on the HTML string and how much control you have over it,
Here is a sample of "MyComponent":
If you don't have much control over the HTML string, you still can use the same approach, you might need to find some elements and inject a target element into the string. |
Great thread guys! I created a simple React Text Highlighter pen that implements some of the ideas in this thread (with some extra magic...), hopefully future visitors can find it useful. |
Here's a solution using 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>; |
Beautiful solution @rehat101 |
Here's what worked best in my case -- splitting by the space character and looking for specific words. It's a little messy in the HTML but who cares 😂 💗 RegExp and map make this soooo clean. 💗💗💗
|
Currently magic numbers for the RegExp at code line 276, but it works. Sourced from @rehat101 at facebook/react#3386
Beautiful @rehat101 |
Brilliant! I find it extremely useful since most of the times you actually need to access the original string |
I refactored the solution of @nullhook a little bit to make it accept variable from a query and memic the behavior of Google search autocomplete 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>
);
}; |
so I found a nice combination of implementing DOMPurify to sanitize html strings with html-react-parser to find targeted markup in it and convert it to JSX components - I use Rebass with passed props as an example.. that came out quite nicely, the two packages will help you to avoid the overhead of rolling your own parser, I just wouldnt recommend this parser package without sanitization. For those struggling with this still maybe scope out this codepen for what it's worth: https://codesandbox.io/s/eager-wiles-5hpff. Feedback on any improvements much appreciated too. |
Hey Jesse,
That seems like a really nice combo for taking text and transforming it to
React components! It would definitely be good for replacing parts of a
string with a component.
Thanks for sharing.
Could also be useful in a JAMstack site to turn Markdown into components.
Best regards,
Derek
Derek R. Austin, PT, DPT, MS, BCTMB, LMT, CSCS
Join me on LinkedIn: https://www.linkedin.com/in/derek-austin/
…On Wed, Feb 26, 2020, 4:35 PM Jesse Lewis ***@***.***> wrote:
so I found a nice combination of implementing DOMPurify to sanitize html
strings with html-react-parser to find targeted markup in it and convert it
to JSX components - I use Rebass with passed props as an example.. that
came out quite nicely, the two packages will help you to avoid the overhead
of rolling your own parser, I just wouldnt recommend this parser package
without sanitization. For those struggling with this still maybe scope out
this codepen for what it's worth:
https://codesandbox.io/s/eager-wiles-5hpff. Feedback on any improvements
much appreciated too.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#3386?email_source=notifications&email_token=AMV42QTV4FDXZIFXCGB3NZDRE3OATA5CNFSM4A5VRBI2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOENB7H7Q#issuecomment-591655934>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AMV42QXARIOKOYSJ4C5IFQLRE3OATANCNFSM4A5VRBIQ>
.
|
This works great. Any way to get it to work in Angular? I've posted a question about it, using your code: |
Hello friends, I need your help please, I am using React Native and what I need to do is from a text that I extract from the DB to apply a format (font color, link) to make @mentions, when searching if in the text finds 1 single match makes replacement all good, but! If there are several @mentions in the text, it throws me an error. /////Text example: hey what happened @-id:1- everything ok ?? and you @-id:2- @-id:3- @-id:4-
in this way the code works well for me only when in the text there is only one mention, example 'hey what happened @-id:1- everything ok ??' , but when placing more than one mention it gives me an error, example: 'hey what happened @-id:1- everything ok ?? @-id:2- @-id:3-' ... Error: TypeError: text.split is not a function and if instead of placing parts = text.split(usrTofind); I place parts = text.toString().split(usrTofind); it gives me an [Object Object] error |
You can use
|
Someone tell me how can replace Example:
i realy don't know who is [Object Object] ? i say "Are you Okay @sara", but this code called someone else ! |
I have almost the same question in |
Nothing worked for me except this package https://www.npmjs.com/package/regexify-string |
I am using this function:
Like that:
|
I just wanted a simple interpolation tool, to replace certain keywords with certain elements. To reduce the number of elements in my output I'm using a 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> Thanks @sophiebits for the idea on |
Based on @pedrodurek's solution, here is something for my specific needs (translations with readable keys instead of just numbers): 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;
});
}; Example: 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>
}) ...becomes... Please accept our <a key="privacyPolicyLink" href="/privacy">Privacy Policy</a> and <a key="termsLink" href="/terms">Terms and Conditions</a>. (The keys are necessary because it actually becomes an array, and react doesn't like array elements without keys.) |
Me too. Tried all of them only this package works. Thank you! |
I have this code which is obviously not working:
Because that would result into a string mixed with objects. Bad I know. But how can I add React components inside a string? All I want is to highlight parts of a string with a react component. A tough case to crack I guess.
The text was updated successfully, but these errors were encountered: