Replace multiple strings within string with react jsx element - reactjs

I have a string called I want to order cheese chicken pizza..
I want to replace cheese with <span style={{color: 'red'}}>cheese</span> and chicken with <span style={{color: 'green'}}>chicken</span>.
So I expect a result like I want to order <span style={{color: 'red'}}>cheese</span> <span style={{color: 'green'}}>chicken</span> pizza.
In an array I have data like let arr = ['cheese', 'chicken']. I took the below approach:-
let text = "I want to order cheese chicken pizza."
arr.map(a=>{
let findText = new RegExp(a, 'g');
text = text.replace(findText, (match)=>(<span>{match}</span>))
});
return text;
I get the result I want to order [object object] [object object] pizza.
I checked maximum solution is for replacing single same string.
I want to achieve this because I will add eventListener to each span tag to get the details.
Can anyone help me out.
Thnaks :)

The challenge, as outlined by Joans, is that you're trying to mix JSX and a string. There are, however, ways around it. The first thing you should do, is convert your array of strings to an object of elements. So this:
let arr = ['cheese', 'chicken']
Should be:
const Elems = {
cheese: (
<span
style={{ color: "red", marginRight: "5px" }}
onClick={() => alert("clicked cheese span")}
>
cheese
</span>
),
chicken: (
<span
style={{ color: "red", marginRight: "5px" }}
onClick={() => alert("clicked chicken span")}
>
chicken
</span>
)
};
Notice how each span element has the onClick attached to them so you can catch the click event. You can add anything else you want.
Then, the idea is to break your text into an array such as:
["I", "want", "to", "order", "cheese", "chicken", "pizza."]
Replace those array elements (cheese and chicken) with the corresponding values inside your Elems object. And then render it. Easy! This function will do that for you:
const swapText = () => {
const text_array = text.split(" ");
const a = text_array.map((item) => {
if (item in Elems) item = Elems[item];
else item += " ";
return item;
});
return a;
};
Then just render it like so:
{swapText().map((item) => item)}
That's it. Here is a Sandbox for you: https://codesandbox.io/s/hardcore-lalande-vxwu6?file=/src/App.js

The issue is that you're dealing with strings and can't insert a span into a string. You could either deal in only JSX or only strings - which is better depends in part on your situation (not sure exactly how you plan on adding the event listener's on the spans). Here is what only strings would look like:
let arr = ['cheese', 'chicken']
let text = "I want to order cheese chicken pizza."
arr.map(a=>{
let findText = new RegExp(a, 'g');
text = text.replace(findText, (match)=>(`<span>${match}</span>`))
});
return text;

Related

#each statement on HTML showing only last item

I need to print in my list all the items of my array, but I'm getting only the last item printed. When I console.log it, I can see all the items. Not sure why this is happening, I tried to figure out using all I've got, but none worked.
let examples = [];
onMount(async () => {
const response = await fetch(
"https://<my-api>/db/v1/cosmos/{container}",
{
method: "POST",
headers: {
"<api-key>":
"<my-api-key>",
},
body: JSON.stringify({
query: "SELECT * FROM c ORDER BY c.name ASC",
}),
}
).then((response) => response.json());
for (let i = 0; i < response.data.length; i++) {
console.log(response.data[i].name);
examples = response.data[i].name;
}
});
On my HTML I'm using the following loop:
{#each examples as example}
<ListgroupItem class="text-base font-semibold gap-2">
Name: {examples} <br />
</ListgroupItem>
{/each}
What I'm getting in return, is just only the last item of my array. If I use example instead of examples, on my foreach loop, my return is only the letters.
When I'm using SvelteKit and Cosmos DB.
Something I don't get is that if the item has 5 letters, it will print 5 rows. If another item has 7 letters, it will print 7 rows. Not sure why. I am not getting any error.
Any ideas what I'm doing wrong?
I also have tried using those below, but they didn't work.
examples.unshift(0);
examples.push(examples);
You are always assigning to what is supposed to be a list, the name of the current item, so at the end you are left with just the name of the last item:
for (let i = 0; i < response.data.length; i++) {
console.log(response.data[i].name);
examples = response.data[i].name;
}
If you only want a list of name this should be replaced with:
examples = response.data.map(x => x.name)
The #each is also wrong, it iterates the variable but you are not using the item, but the list variable, it should be:
{#each examples as example}
<ListgroupItem class="text-base font-semibold gap-2">
Name: {example} <br /> <!-- example, not examples -->
</ListgroupItem>
{/each}
(As examples was just a string before, it iterated the letters in the string.)

Need to loop the Icon and display Star Icon 5 times using React [duplicate]

I am using React/JSX and Lodash in my app in order to accomplish what I want.
I need to repeat an element a certain amount of times depending on a condition. How should I do that?
Here is the element:
<span className="busterCards">♦</span>;
And I am assigning it like this:
let card;
if (data.hand === '8 or more cards') {
card = <span className='busterCards'>♦</span>;
}
So in this case, I need to repeat the element 8 times. What should be the process using Lodash?
The shortest way to do this without any external libraries:
const n = 8; // Or something else
[...Array(n)].map((e, i) => <span className="busterCards" key={i}>♦</span>)
solution without lodash or ES6 spread syntax:
Array.apply(null, { length: 10 }).map((e, i) => (
<span className="busterCards" key={i}>
♦
</span>
));
Here you go:
let card = [];
_.times(8, () => {
card.push(<span className="busterCards">♦</span>);
});
You may want to add key to each span element so React won't complain about missing the key attribute:
let card = [];
_.times(8, (i) => {
card.push(<span className="busterCards" key={i}>♦</span>);
});
For more info about .times, refer here: https://lodash.com/docs#times
Implementing this without Lodash
<section>
{Array.from({ length: 10 }, (_, i) => <span key={i}>Your text</span>)}
</section>
How does this work?
Array.from() is used in two contexts:
Creating an array from an array-like data structure. For example, we can convert a map into an array using Array.from()
const map = new Map([ [1, 2], [3, 4], [4, 5] ])
console.log(Array.from(map)) //gives an array - [[1, 2], [3, 4], [4, 5]]
Creating an array and filling out the values (This can be handy when we need to create an array containing more elements)
Array.from() accepts an object and a callback function.
Array.from({ length: 7 }, (() => 10)) // gives [10,10,10,10,10,10,10]
We can take advantage of the index (second parameter) inside the callback function to provide unique array elements
Array.from({ length: 4 }, ((_, i) => i + 1)) // [1,2,3,4]
I'm using this and works for me.
[...Array(10)].map((elementInArray, index) => (
<div key={index}>
Text in Loop
</div>
))
Using _.times: https://jsfiddle.net/v1baqwxv/
var Cards = React.createClass({
render() {
return <div>cards {
_.times( this.props.count, () => <span>♦</span> )
}</div>;
}
});
You could do it like this (without lodash):
var numberOfCards = 8; // or more.
if (data.hand >= numberOfCards) {
var cards = [];
for (var i = 0; i < numberOfCards; i++) {
cards[i] = (<span className="busterCards">♦</span>);
}
}
Straight forward options ways to do that without any external libraries (2021):
// straight forward but without key index. Not so good for react but work fine with worning
Array(X).fill(<span className="busterCards">♦</span>)
// with index
Array(X).fill().map((v,i)=> <span className="busterCards">♦</span>)
Array.from( Array(X), (v,i) => <span key={i} className="busterCards">♦</span> )
// same thing basically
Array.from( {length:X}, (v,i) => <span key={i} className="busterCards">♦</span> )
[...Array(3)].map( (_,i)=> <span key={i} className="busterCards">♦</span> )
You can create an array with as many items as you need rendered and then map through the array to render the correct number of elements you need.
const totalItems = 8;
const items = new Array(totalItems).fill(null);
// .... then
return (
{items.map((_, idx) => <span className="busterCards" key = {idx}>♦</span>)}
);
You can use Lodash range.
_.range(20).map((_n, i) => <MyComponent key={i}/>)
I thought, that if someone would like to use it in many places in the code, it would be a good idea to make it an npm package: https://www.npmjs.com/package/repeat-component. I think it will help someone :)

Comma to New Line

In React, how can I turn , characters into new lines?
Suppose we have an array like this:
const items = [
{
label: "Animals",
value: "Puppies, Kittens, Bunnies"
},
// ...
];
And we display it like this:
<div>
{items.map(item => (
<div style="left">
{item.label}
</div>
<div style="right">
{item.value}
</div>
))};
</div>
How can I turn all , characters in the value keys of the array items into new lines?
Current Output:
Animals Puppies, Kittens, Bunnies
Desired Output:
Animals Puppies
Kittens
Bunnies
{item.value.split(", ").map((line, i) => <div key={i}>{line}</div>)}
is the simplest, if putting each item in a div is okay for you.
The other, more complex option is to add <br>s between each line, and wrap those in a React.Fragment:
function addBrs(items) {
const children = [];
items.forEach((item) => {
children.push(item);
children.push(<br />);
});
children.pop(); // Remove last extraneous BR
return React.createElement(React.Fragment, {}, ...children);
}
// ...
{addBrs(item.value.split(", "))}}

Why cannot use 'map or filter' in testing library with typescipt?

I cannot use to array method for testing library.
My test code is like below.
const ListTitle = fakeData.forEach((el) => {
el.filter((element: string | object) => (typeof element === "string" ? element : element?.props?.children)); **** filter method has a error ts.2339
});
Attached, I want to exclude only text in fakeData array.
fakeData is like below the code.
interface FakeDataProps {
id:number
titleChild:JSX.Element
}
const fakeData: FakeDataProps[] = [
{
id: 1,
titleChild: (
<>
party goes on <span style={{ fontWeight: 500 }}> The end</span>
</>
),
},
{
id: 2,
titleChild: (
<>
party goes on <span style={{ fontWeight: 500 }}>The end</span> Your time
is ...{" "}
</>
),
},
{
id: 3,
titleChild: (
<>
party goes on <span style={{ fontWeight: 500 }}>The end</span>
</>
)
}
];
I expected
[
"party goes on The end",
"party goes on The end Your time is ...",
"party goes on The end",
]
But, In my test code, There are error occured that has "Property 'filter' does not exist on type 'FakeDataProps'.ts(2339)"
What shall I do?
The typescript error is occurring because you're calling .filter() on an individual item. You should be calling it on the array of items like so:
const ListTitle = fakeData.filter((element: string | object) => (typeof element === "string" ? element : element?.props?.children));
There is no need for .forEach() in your example, just use .filter() as above.

Creating an array of styles in React

For certain reasons I need to create an array of different styles to eventually use at certain times. Regardless I have this bit of code...
export const carouselData = {
cdata: [{
bgimage: require('Assets/img/Banners/mybanner1.jpg')
},{
bgimage: require('Assets/img/Banners/mybanner2.jpg'),
}]
}
...
var mySectionStyle
this.props.cdata.cdata.map((carouselData, key) => (
mySectionStyle[key] = {
backgroundImage: "url(" + carouselData.bgimage + ")"
}
))
return (
{ this.props.cdata.cdata.map((carouselData, key) => (
<div className="bg_image" style={ sectionStyle[key] }>
//Some stuff here
</div>
))}
)
Now to anyone that is half decent at coding probably sees huge issues with this code but as a newbie I need help finishing it (or rewriting).
Can anyone help me create an array so I can access my styles one by one with mySectionStyle[0], mySectionStyle[1], mySectionStyle[2] etc
Edit. I have an array that has many images in it and I want those in an array so I can set the carousel up with different background images.
Why can't you just do:
var mySectionStyle = {
style1: {
margin: 0,
},
style2: {
margin: 10,
},
}
const style1 = mySectionStyle['style1'];
const style2 = mySectionStyle['style2'];
If you later need it in an array, you can use the Object methods to convert it.
const availableStyles = Object.keys(mySectionStyle); // ['style1', 'style2']
availableStyles.forEach(style => mySectionStyle[style].backgroundImage = `url(${carouselData.bgimage})`;);
See also Object.values and Object.entries for other conversion to array options.

Resources