iansinnott / react-string-replace Goto Github PK
View Code? Open in Web Editor NEWA simple way to safely do string replacement with React components
License: MIT License
A simple way to safely do string replacement with React components
License: MIT License
I have this string:
let text = "{form 1} {form 2} {form 1:3}";
and this regexp:
/{form (\d+(:\d+)?)}/g
that should match {form x} and {form x:y}
I am executing:
text = reactStringReplace(text, /{form (\d+(:\d+)?)}/g, (match, i) => { return 'match: ${match}, i: ${i}'; });
then in the return of my component I have this:
return (
<div className={"cfs-dove__thread-content-message"}>
{replacedText &&
replacedText.map((el, i) => {
return (
<p key={`lettercontent${i}`} dangerouslySetInnerHTML={{ __html: el }}></p>
);
})}
</div>
);
and this is de output:
<div class="cfs-dove__thread-content-message">
<p>match: 1, i: 1</p>
<p>2</p>
<p>match: 1:3, i: 7</p>
<p>:3</p>
<p>match: , i: 9</p>
</div>
As you can see, the first and third <p>
are fine. But the rest aren`t.
The second is the matched value of {form 2} but didn't enter in the replace function.
The forth is only part of the matched value of {form 1:3} and also didn't enter in the function.
And the last did enters in the replace function but the match is "";
I am having an issue below that when I run the code below a match is found but an empty string is returned for the match.
//code
let newStr = reactStringReplace(str, /\[img.*?]/g, (match, i) => (
<img key={"processed: " + match + i} src={match.replace('[img src="', " ").replace('"]', "")} />
));
//input
[img src="http://i.imgur.com/GBpbHPh.png"]
Any advice?
For example, /(“|")[^(”|")]+(”|")[⁰¹²³⁴⁵⁶⁷⁸⁹]+|[⁰¹²³⁴⁵⁶⁷⁸⁹]+|\S+[⁰¹²³⁴⁵⁶⁷⁸⁹]+/gm doesn't select the correct text
After called into replace function, it will be outputting an array that causing regex evaluation to be incorrect.
test('Works with splitted element', t => {
const input = ['hey there', 'you', 'again'];
t.deepEqual(
replaceString(input, 'there you', x => `(${x})`),
['hey ', '(there you)', 'again']
);
});
When i install trought npm install react-string-replace, in source code isn't index.d.ts file and npm install @types/react-string-replace doesn't working.
Can you add support for multiple regex capture groups?
This is issue was raised in #1. There's currently no clean way to run multiple replacements on a single string. The example raised in #1 is a good one: Given the below string, we want to match and replace URLs, hashtags and @-mentions:
Hey @iansinnott, check this link https://github.com/iansinnott/ #github
I'm not yet sure what the ideal API should look like, but at the very least reactStringReplace
should accept a an array is an argument, allowing multiple replacements on the same string.
const reactStringReplace = require('react-string-replace');
const originalTweet = 'Hey @iansinnott, check this link https://github.com/iansinnott/ #github';
// Match URLs
let reactReplacedTweet = reactStringReplace(originalTweet, /(https?:\/\/\S+)/, (match, i) => (
<a href={match}>{match}</a>
));
// Match @-mentions
reactReplacedTweet = reactStringReplace(reactReplacedTweet, /(@\w+)/, (match, i) => (
<a href={`https://twitter.com/${match}`}>@{match}</a>
));
// Match hashtags
reactReplacedTweet = reactStringReplace(reactReplacedTweet, /(#\w+)/, (match, i) => (
<a href={`https://twitter.com/hashtag/${match}?src=hash`}>#{match}</a>
));
I'm trying this out in react native. It finds the pattern, the index and the offset, but the 'match' is always empty.
sample string: "This is a test comment #123123"
reactStringReplace(props.comment.body, /[#]\d+/gm, func)
const func = (match, index, offset) => { console.log('match:', match); console.log('index:', index); console.log('offset:', offset); return <TouchableHighlight key={match + index}><Text>{match}</Text></TouchableHighlight>; }
Match is empty, but if I replace it with something hardcoded like "XXX" it will display.
I am trying to use your library this way to detect URLs in a string and reformat with some react components:
const LINK_DETECTION_REGEX = /(([a-z]+:\/\/)?(([a-z0-9\-]+\.)+([a-z]{2}|aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|local|internal))(:[0-9]{1,5})?(\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.%]*)(\?[a-z0-9+_\-\.%=&]*)?)?(#[a-zA-Z0-9!$&'()*+.=-_~:@\/?]*)?)(\s+|$)/gi;
formattedMessage = reactStringReplace(messageContent, LINK_DETECTION_REGEX, function(match, i) {
console.log(match)
...
})
The problem I have is that this regex makes the callback trigger 3 times with a standard URL such as: http://i.imgur.com/Pw4Xk7H.gif
First time with match === "http://i.imgur.com/Pw4Xk7H.gif"
(which is what I want)
But then with match === "i.imgur.com"
And again with match === "com"
After that it just crashes with the error:
Unhandled rejection TypeError: Cannot read property 'length' of undefined
at replaceString (webpack:///./~/react-string-replace/index.js?:51:34)
Is it a bug? Is my regex wrong? (it was working with javascript replace)
Can someone investigate on this? Thanks
Thanks for the library.
Is there any workaround to chain multiple calls to reactStringReplace ?
TypeError: First argument to react-string-replace must be a non-empty string
Would you accept a PR that removes the lodash requirement?
!isRegExp(re)
and isString
can be replaced with typeof re === 'string'
flatten
can be replaced with flatMap
(as long as IE11 support is not required, but you could specify that a polyfill is needed for IE11 support)escapeRegExp
can just be inlinedHi, I found some test cases where the function is failing:
reactStringReplace('Apt 111, phone number 555-5555-555.', (match, i) => ( console.log(
<span key=${i} style={{ color: 'red' }}>${match}) ));
so what I did I removed the second parameter and checked, so function is passed as matched and fn is passed as undefined. And got the below error:
F:\github\test1\react-string-replace\node_modules\react-string-replace\index.js:11
? string.replace(reRegExpChar, '\$&')
^
TypeError: string.replace is not a function
So, I added some validation checks for the arguments so that instead of exiting with an error it provides the user with the warning message and exists swiftly.
PS F:\github\test1\react-string-replace> node app
Debugger attached.
Apt 111, phone number 555-5555-555.
function
undefined
Please check the function arguments, either the match or the function is missing
Waiting for the debugger to disconnect...
thanks,
Niki
Also, I have added the fix in my local system and wanted to check in with you
Not sure how big a lift this would be, but something to think about.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace
const reactStringReplace = require("react-string-replace");
var array = reactStringReplace("whats your name", "your", (match, i) => (
<span key={i} style={{ color: "red" }}>
{match}
</span>
));
I'm running the above code (which is from the example) and I get [object Object]. Unfortunately, this is not what the example says I should be getting. What am I doing wrong here?
Currently using React Native v0.61 and [email protected]
Thanks!
It would be great if I could provide an array of matches:
reactStringReplace('whats your name', ['whats', 'your'], (match, i) => (
<span>{match}</span>
));
Which would return:
<span>whats</span> <span>your</span> name
Update: added PR: #16
per react:
/**
* @deprecated Use either ReactNode[]
if you need an array or Iterable<ReactNode>
if its passed to a host component.
*/
Possible to refactor?
The return type for the "reactStringReplace" function is "React.ReactNodeArray" which is deprecated now. You can use "ReactNode[]".
Hello,
is it possible use this library with ES6 Imports?
I have tried something like this:
import { reactStringReplace} from "react-string-replace";
But it doesn't work because I get error reactStringReplace is not a function
How can I fix this? :)
Thank you.
If you pass a regExp as second argument in function reactStringReplace
you can get error like: Cannot read property "length" of undefined
. I think thiss is caused by the work of vanilla replace
method.
For example, this code give us an error (it's cyrillic):
const str = 'Справка Форма Ф9';
const re = /(справка[а-я].?(по)? форм[а-я] [ф|Ф]\d)/gi
console.log(str.split(re)); // ["", "Справка Форма Ф9", undefined, ""]
So you cannot get length proprty of undefined. And this error will crush you page.
I'm having trouble using reactStringReplace
to format text with bold/italic tags. I have it set up as follows:
var text1 = "[b]Testing bold [i]and italic[/i] tags[/b]"
var text2 = "[b]Testing bold [i]and italic tags[/b][/i]"
let replaced = reactStringReplace(text1, /\[b\]([\s\S]+)\[\/b\]/g, (match, i) => {
return <b key={i}>{match}</b>
})
replaced = reactStringReplace(replaced, /\[i\]([\s\S]+)\[\/i\]/g, (match, i) => {
return <i key={i}>{match}</i>
})
// result (html)
<b>Testing [i]bold and italic[/i] tags</b>
<b>Testing [i]bold and italic tags</b>[/i]
I'm not sure if this is a problem with reactStringReplace
, with the regex I'm using, or something else. If I apply the italic replace first, I get italic tags where I'd expect them to be, but the [b] tags remain unchanged. Is this use case possible using reactStringReplace
or do I need to use dangerouslySetInnerHtml
?
Bonus: is reactStringReplace
capable of handling unbalanced tags, or improperly formatted tags as in text2
or should I be doing some pre-processing to ensure the strings are properly balanced and formatted?
https://github.com/iansinnott/react-string-replace/blob/master/package.json#L34-L37
"dependencies": {
"lodash.escaperegexp": "^4.1.1",
"lodash.flatten": "^4.2.0",
"lodash.isregexp": "^4.0.1",
"lodash.isstring": "^4.0.1"
},
Consider using:
https://github.com/lodash/babel-plugin-lodash (for package dev)
https://github.com/lodash/lodash-webpack-plugin (for app development)
Per method packages have been discontinued, https://github.com/lodash/lodash/wiki/Roadmap.
Discontinue per method packages in favor of modular lodash
Hi,
I saw you answered a similar issue already (#19 ), and for WEbpack this solution works fine. But with for example Rollup it doesn't work.
Error: 'default' is not exported by node_modules/react-string-replace/index.js
So would you accept a PR where we add ES6 compatibility?
A 4th parameter as callback to process non-match items would be useful.
For instance, in ReactNative, plain text cannot be outside of <Text>
components. The non matches need to be wrapped in a component as well. A callback for these would come quite handy.
Example:
const matchCallback = (match, i) => (<Text key={i} style={styles.highlight}>{match}</Text>);
const nonMatchCallback = (nonmatch, i) => (<Text key={i}>{nonmatch}</Text>);
const components = reactStringReplace(input, regex, matchCallback, nonMatchCallback);
Hi there, when using this twice on the same string what is happens is as follows, the first string matches and an array containing the react component returned from the callback fucktion is returned, and than in the second replace, it skips the symbol that was returned from the first run because it is not a string...
I think the solution would be to use flattenDeep instead of flatten, and clarify that callback functions should return an array instead of a component.
Hi, I your package helped me in a project where I wanted to wrap fields in a text. Fields were used to replace the matched strings with something else. However, I was missing the information about index of the match start character within the text. I will reference my solution.
Hello! First of all, thanks for the nicest replacer for react. )))
There's a thing. I want to use index parameter in replacer function as a key for react to be happy and render all my replaced texts. If I do two replaces at a time, say
const firstStepTexts = reactStringReplace(myText, regex1, (match, i) => <span key={`first_${i}`}>{match}</span>)
const second = reactStringReplace(firstStepTexts, regex2, (match, i) => <span key={`second_${i}`}>{match}</span>)
Then, for initial string in pattern of ${match_second_regex_1} ${match_first_regex} ${match_second_regex_2}
replacer would set the same key for both second regex matches, and then react would blow up swallowing the second match span entirely.
What I did for now was embedding offset
param from the new lib version, but that's not the real solution obviously.
What I could do right now is to call .reduce
with running total index and not calling reactStringReplace on array, but that way I'm missing perfect lib api use-case. From the api point of lib, I could think of two possible things: 1) having second argument for replace function be running index, and not exact string match index; or 2) having another one argument with that index for replacer function.
First option would almost definitely break someone's workflow (https://xkcd.com/1172/), second one makes api clunky (arghhh, fourth parameter).
What's your opinion on this? I would be happy to help with code, but messing with api without your decision feels not a way to go.
Thanks for a very cool function! What I'm trying to do is replace a user selected string in a body of text with a React component (that contains the selected string). The issue I'm running into is that react-string-replace is replacing all instances of a selection, rather than just the first match.
Since I'm targeting user-selected text (via the Selection API), I'm using a RegExp constructor, like so:
const key = uuid(); // generate unique key
const textRegex = new RegExp(`(${window.getSelection().toString()})`); // capture user selection
const updatedText = ReactStringReplace(postText, textRegex, match => {
return <HighlightSpan text={match} key={key} />; // return React component
});
// other stuff
This works absolutely perfectly if the selected string is unique in the body of text, but if there is another match then I run into a duplicate key error (and also highlight multiple selections when I only want the first).
Since the RegExp constructor doesn't include a 'g' flag, as far as I understand it should only match the first occurrence - but I might be mistaken as to how react-string-replace works! Is it possible to have it run only once, and if so could you please give me a tip on how to achieve this?
Thanks in advance,
Jake
Just actually checked the code and realized that the g & i flags are set by default - perhaps I could make a pull request to have the regex flags optional?
Hey there! Dropping the dependencies in #71 was a great improvement. Looks like you tagged v0.5.0 on GitHub, but npm's most recent publish is v0.4.4 from 3 years ago.
Could you publish v0.5.0 to npm?
Aside, the package seems to be in a fairly stable state and used by a lot of people. Would it be worthwhile bumping to a stable v1?
thanks!
When you replace a match in the beginning/end of a string you get an extra empty item in the array which then fails subsequent requests to reactStringReplace
let text = reactStringReplace('[player:1] versus [player:2]', /\[player:(\d+)\]/g, (match, i) => (
<span key={i}>Player {match}</span>
));
Returns ["", Object, " versus ", Object, ""]
which hinders further chaining since it fails if first item in array is "empty"
Currently, following code:
function nl2br(content: string | undefined) {
return reactStringReplace(content || '', /\n/g, (match, index) => (
<React.Fragment key={index}>
<br />
{match}
</React.Fragment>
));
}
const output = mount(<div>{nl2br('a\n\nb\nc\n')}</div>);
expect(output.html()).toEqual('<div>a<br><br>b<br>c<br></div>');
throws an error on the expect
line:
Expected: "<div>a<br><br>b<br>c<br></div>"
Received: "<div>a<br>b<br>c</div>"
Which means, the \n\n
gets replaced into only one <br />
instead of two;
Also, the last \n
in the didn't get replaced at all.
Also, why do I have to do this:
(match, index) => (
<React.Fragment key={index}>
<br />
{match}
</React.Fragment>
))
?
I'd expect from the documentation that
(match, index) => (
<br key={index} />
))
would be enough, but then some text between the newlines get "swallowed" (it doesn't appear in the final string).
I'm doing this:
<div>
{reactStringReplace('abc___def', /(___)/g, (match, i) => (
<input key={i} >{match}</input>
))}
</div>
I get an error in the console which doesn't give any more information. Prior to the error I get the warning: "Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function."
It is the input field which is the problem - it works with a span.
The container function is a functional component.
reactStringReplace(title, /#(\w+)/g, (match, i) => (
<a key={match + i} data-hash={match} onClick={this.onclick} >#{match}
))
let replacedContent
const test = '@username http://a_photo.jpg'
replacedContent = reactStringReplace(test, /(http?:\/\/.*\.(?:png|jpg))/g, (match, i) => (
<Text key={i}>Image</Text>
))
replacedContent = reactStringReplace(replacedContent, /@(\w+)/g, (match, i) => (
<Text key={i} style={style.usernameRef}>@{match}</Text>
))
Will throw First argument to react-string-replace#replaceString must be a string
7.12.3
to 7.12.4
.This version is covered by your current version range and after updating it in your project the build failed.
eslint-plugin-react is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.
no-unused-prop-types
: avoid a crash (#2131, @ljharb)prop-types
: avoid further crashes from nonexistent nodes in unusedPropTypes (#2127, @ljharb)prop-types
: Read name of callee object (#2125, @CrOrc)prop-types
: Ignore reassignments when matching props declarations with components (#2051, #1957, @yannickcr)prop-types
, no-unused-prop-types
, require-default-props
: Detect components with return statement in switch/case (#2118, @yannickcr)The new version differs by 10 commits.
433cc3f
Update CHANGELOG and bump version
536bc35
[Tests] prop-types
: add case from #2134
df7ffc1
[Tests] no-typos
: test case from #2136
c7e5f38
[Tests] improve version detection tests.
2dd2277
[Tests] prop-types
: add now-passing test case
84652b6
[Fix] no-unused-prop-types
: avoid a crash
58ed9e9
[Fix] prop-types
: avoid further crashes from nonexistent nodes in unusedPropTypes
7f7b96d
[Fix] prop-types
: Read name of callee object.
5fc50aa
[Fix] Ignore reassignments when matching props declarations with components
ba80a4c
[Fix] Detect components with return statement in switch/case
See the full diff
There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.
Your Greenkeeper Bot 🌴
Hi there 👋
I have a situation where I'd like to replace the matched regex with my own array of nodes respectively. It would be great receiving natural index numbers 0, 1, 2
on callback instead of odd ones.
const nodes: React.ReactNodeArray
// [
// <span key='something'>label</span>,
// <h1 key='otherthing'>heading</h1>
// ]
reactStringReplace('These are my {} and {} and {} values.', /({})/g, (match, i) => {
// odd index numbers
return nodes[(i - 1) / 2]
})
I think the newly added type definitions are not quite correct (at least my otherwise correctly working code now throws type errors after upgrading).
I do something like:
reactStringReplace(<whatever>).filter(part => !!part)
to drop some empty strings, because they would break some stuff afterwards.
Obviously reactStringReplace
returns an Array of some Sort
return flatten(source.map(function(x) {
return isString(x) ? replaceString(x, match, fn) : x;
}));
The return type in the type definitions is set to JSX.Element
which extends React.ReactElement
.
So on one hand, this can't be an Array and on the other hand it cannot even be a string.
I suppose the return Type should be at least JSX.Element[]
or furthermore (JSX.Element | string)[]
to capture the real return type of reactStringReplace
.
For simplicity one could use React.ReactChild[]
since ReactChild
allows ReactElement
as well as string
or number
.
Correct me if I'm missing something.
I am willing to submit a pull request regarding this issue, I just wanted to ask first in case I'm wrong.
I am using react-string-replace for translations. I have a string and I want to replace a couple of substitutes.
The code looks like this:
ReactStringReplace(
'[link]This is my link[/link] available everywhere or [shortcode].',
/(\[link\].*\[\/link\])|(\[shortcode\])/g,
(match) => {
console.log('match', match);
}
)
From my understanding, my regular expression should match only the first link and the [shortcode]
. But the result in the console looks like this:
match [link]This is my link[/link]
match available everywhere or
match [shortcode]
I don't understand why is the middle match there. It doesn't match the regular expression.
How can i make multiple matches in a sentence?
let sentence = "Hello Good Morning America"
ReplaceString(sentence, "Good" || "America", match =>
<Text style={{ color: 'violet' }}>{match}</Text>)
I want the final result to be, the word "Good" and "America" should have the fontColor of violet and the rest is default which is black. How can i achieve something like this? I tried the above code but the word "America" is the only being changed
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.