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(", "))}}
Related
I have an array that contains as follows. I have successfully mapped the data and put it into the elements. but there are some values I couldn't get correctly. I have added a console.log to figure out the data. data is correct in the array, I want to get the Seats ("A1,B1") in here <h5><small>{seatNos.seats}</small></h5> but nothing is displaying. appreciate any help.
Data array
"data": {
"userBookings": [
{
"transactionId": 6357604,
"totalAmount": 350,
"createdAt": "2021-08-05 02:16:48",
"movieName": "Mortal Kombat",
"theaterName": "XxX Cinema",
"showTime": "17:30",
"refcode": "0016048GIN210805I",
"bookedSeats": [
{
"seatType": "Comfert",
"seats": "A1,B1",
"childTickets": 1,
"totalTickets": 2,
"bookedDate": "2021-08-05"
}
]
}
]
},
code
<div className="col-xl-5 col-lg-5 col-md-7 col-sm-7 col-xs-9 col-6" style={{paddingLeft:25}}>
<h5><b>{bookingsData.movieName}</b></h5>
<h5>{bookingsData.theaterName}</h5>
<h5><small>{bookingsData.showTime}</small></h5>
{bookingsData.bookedSeats.map((seatNos) => {
console.log(seatNos.seats);
<h5><small>{seatNos.seats}</small></h5>
})}
{/* <h5><small>{bookingsData.bookedSeats.seats}</small></h5> */}
</div>
You need to return element in map, and set key for this element:
{bookingsData.bookedSeats.map((seatNos, index) => {
console.log(seatNos.seats);
return <h5 key={index}><small>{seatNos.seats}</small></h5>
})}
Your arrow function inside the .map() doesn't return a value. You need a return before the JSX:
{bookingsData.bookedSeats.map((seatNos) => {
console.log(seatNos.seats);
return <h5><small>{seatNos.seats}</small></h5>
})}
Or to use the implicit return arrow function syntax (Either round brackets, or no brackets: () => () or () => returnedValue)
{bookingsData.bookedSeats.map((seatNos) => <h5>
<small>{seatNos.seats}</small>
</h5>)}
This is because you forgot the return
array.map((item) => {
return (
<p>{item.value}</p>
)
})
Hello guys i wanted to render some items into their own category using .map, but i dont know how to do it . I only know how to render standard list. Below is the example of my data:
[
{
"id_kategoriTest": 1,
"kategori": "Hematologi",
"id_jenisTest": 24,
"jenis": "Coombs Test Direct"
},
{
"id_kategoriTest": 1,
"kategori": "Hematologi",
"id_jenisTest": 25,
"jenis": "Hb Elektroforesis"
},
{
"id_kategoriTest": 14,
"kategori": "Imuno-Serologi",
"id_jenisTest": 247,
"jenis": "Anti HBs Titer"
},
{
"id_kategoriTest": 14,
"kategori": "Imuno-Serologi",
"id_jenisTest": 248,
"jenis": "Anti HBc"
}
]
The output i wanted is like this:
Hematologi
Coombs Test Direct
Hb Elektroforesis
Imuno-Serology
Anti HBs Titer
Anti HBc
Thanks.
First step is to sort your array to something more convenient.
Then render the sorted array.
Here, myArray contain your array above
const sorterAR = [];
myArray.forEach((item) => {
let cat = sorterAR.find(
(cat) => cat.id_kategoriTest === item.id_kategoriTest
);
if (!cat) {
cat = {
id_kategoriTest: item.id_kategoriTest,
kategori: item.kategori,
items: [],
};
sorterAR.push(cat);
}
cat.items.push(item);
});
return (
<div className="App">
{sorterAR.map((cat) => (
<>
<div>
<b>{cat.kategori}</b>
</div>
<ul>
{cat.items.map((item) => (
<li>{item.jenis}</li>
))}
</ul>
</>
))}
</div>
);
I just can't work out how to list/loop through items from an array into my component. All the online tutorials and SO answers make sense, but my code won't respond in a like fashion.
My scenario is this: A user selects an option from a menu and...
switch(which){
:
case 'who': {
this.getStaffList('stafflist');
break;
}
:
the database is called and returns an array...
getStaffList(value:string){
this.targetID = value;
this.service.getStuff(this.targetID).subscribe(
items => {
console.log(items[0].fname); <---this yields 'Sue'
this.title = "Your staff list";
}, error => {
}, () => {
}
);
}
The PHP view of the array (before JSON.encode) is:
Array
(
[0] => Array
(
[userID] => 6551
[certID] => SB287
[fname] => Sue
[lname] => Bennett
)
[1] => Array
(
[userID] => 6568
[certID] => MF6568
[fname] => Marion
[lname] => Ferguson
)
:
Back in Angular, the very simple template is:
<div id="stafflist" class="mainbox" *ngIf="bListStaff">
<div class="panel panel-info">
<div class="panel-heading">
<div class="panel-title">{{title}}</div>
</div>
<div style="padding-top:25px" class="panel-body">
{{items[0].fname}} <!--This generates a '..._co.items is undefined' error
<ul>
<li *ngFor="let item of items; let i = index">{{i}} {{item}}</li> <--- this yields nothing/zilch
</ul>
</div>
</div>
</div>
The '..._co.items is undefined' error that I'm receiving suggests that the items array isn't known outside of getStaffList, but I don't understand why that is (if it is) and don't understand what's missing in my approach.
You are not assigning items inside the subscribe, create a variable named items of type any and assign the value inside the subscription,
getStaffList(value:string){
this.targetID = value;
this.service.getStuff(this.targetID).subscribe(
items => {
this.items = items;
this.title = "Your staff list";
}, error => {
}, () => {
}
);
}
also since the request is asynchronous use safe navigation operator to check if the value is present before the values are being assigned,
<div style="padding-top:25px" class="panel-body">
{{items[0]?.fname}} <!--This generates a '..._co.items is undefined' error
<ul>
<li *ngFor="let item of items; let i = index">{{i}} {{item}}</li> <--- this yields nothing/zilch
</ul>
</div>
Just define a property items in your component class and set it like this:
getStaffList(value:string){
this.targetID = value;
this.service.getStuff(this.targetID).subscribe(
items => {
console.log(items[0].fname); <---this yields 'Sue'
this.title = "Your staff list";
this.items = items;
}, error => {
}, () => {
}
);
}
The scope of the items in your subscribe is only inside that function so it's not accessible from the outside.
I am creating a list of list and want to put a unique key for each element. When I use the React Dev Tool, the new key is "2016-10,-,football".
Why does it have commas in it?
What is the correct way to specify a key when I want "2016-10-football"?
React Dev Tool Console
import React from 'react'
import ReactDOM from 'react-dom'
const dates = ['2016-10', '2016-11', '2016-12'];
const sports = ['football', 'baseball', 'basketball'];
const Dates = ( { dates, sports } ) => {
return (
<ul>
{ dates.map( date => {
return (
<div key={date.toString()} >
<li>{date}</li>
<Sports sports={sports} date={date}/>
</div>
)
})
}
</ul>
)
}
const Sports = ( { date, sports } ) => {
return(
<ul>
{ sports.map( sport => {
// Results in: key="2016-10,-,football"
// Expected: key="2016-10-football"
return (<li key={[date, '-', sport]} >{sport}</li>)
})}
</ul>
)
}
ReactDOM.render(<Dates dates={dates} sports={sports}/>, document.getElementById('main'))
key expects a string so when you pass an array you are calling the Array's .toString() function. You will see the same result if you do console.log([date, '-', sport].toString())
Replace [date, '-', sport] with date + '-' + sport to fix it.
It's showing with commas because toString will use commas to join the array.
This is what you have:
arr = ['2016-10', '-', 'football']
console.log(arr.toString); // "2016-10,-,football"
This is what you want:
arr = ['2016-10', '-', 'football']
console.log(arr.join()); // "2016-10-football"
So consider replacing the li to (notice the .join()):
return (<li key={[date, '-', sport].join()} >{sport}</li>)
edit: use join("") for expected result, you should pass a separator (in this case an empty string) to arguments of the method. For example, ['2016-10', '-', 'football'].join('~separator~') would return "2016-10~separator~-~separator~football"
Added some examples for better understanding
key={'company_'+index} // key={date +'-'+sport}
<TableCell key={'company_'+index} align="right">
{row.company?.name}
</TableCell>
return(
<ul>
{ sports.map( sport => {
// Results in: key="2016-10,-,football"
// Expected: key="2016-10-football"
return (<li key={date +'-'+sport} >{sport}</li>)
})}
</ul>
)
I had no problem using a plus sign to concatenate two fields to make a unique key:
{rows.map((Group) => (
<li key={Group.user_id + Group.lgroup_id}>
-- Display the parts of the Group object here. --
</li>
))}
So I'm attempting to render multiple input fields with React.
Everything looks fine until I remove an item. Always the last item is being "removed". If you want to try my code, write "A" in input field 1, "B" in 2, "C" in 3 and remove "B". You'll notice that you have removed "C" instead.
I have tried both value and defaultValue for input to no avail. I have also tried giving a name to the input. I think I am missing a key point here.
Any recommendations?
var MultiInput = React.createClass({
getInitialState: function() {
value = this.props.value
// force at least one element
if (!value || value == '') {
value = [ null ]
}
return {
value: value
}
},
getDefaultProps: function() {
return {
}
},
add_more: function() {
new_val = this.state.value.concat([])
new_val.push(null)
this.setState({ value: new_val })
},
remove_item: function(e, i) {
new_state = this.state.value.concat([])
new_state.splice(i,1)
this.setState({ value: new_state })
},
render: function() {
me = this
// console.log(this.state.value)
lines = this.state.value.map( function(e, i) {
return (
<div key={i}>
<input value={e} />
<button onClick={me.remove_item} >X</button>
</div>
)
})
return (
<div>
{lines}
<button onClick={this.add_more}>Add More</button>
</div>
)
}
})
There are a few things going on here.
To start, you shouldn't use the array index as the key when rendering in an array:
lines = this.state.value.map( function(e, i) {
return (
<div key={i}>
<input value={e} />
<button onClick={me.remove_item} >X</button>
</div>
)
})
The first time through, ["A", "B", "C"] renders:
<div key={0}>
...
</div>
<div key={1}>
...
</div>
<div key={2}>
...
</div>
Then, the second time, once you've removed "B" and left ["A", "C"], it renders the following:
<div key={0}>
...
</div>
<div key={1}>
...
</div>
So, when you removed item at index 1, the item previous at index 2 moves to index 1. You'll want to use some unique value that doesn't change when the position in the array changes.
Second, you should use the empty string instead of null for initialization, and then you'll see that you can't type anything in your inputs. That's because value ensures that an input's value is always whatever you pass it; you'd have to attach an onChange handler to allow the value to be edited.
Changing to defaultValue allows you to type in the box, but when you type, the string in this.state.value doesn't get updated--you'd still need an onChange handler.
Finally, your button has an onClick of this.remove_item, but your remove_item method seems to take the event and index as parameters. However, React will not pass the current index to remove_item; you would need to create a new function that passes the correct params:
onClick={me.remove_item.bind(null, i)}
That said, you really shouldn't call Function#bind inside render as you'll create new functions every time it runs.
Working Code
#BinaryMuse clearly explains why my code above doesn't work: by removing an item from the array and render is called again, the items change position and apparently React's algorithm picks the "wrong changes" because the key we're providing has changed.
I think the simplest way around this is to not remove the item from the array but rather replace it with undefined. The array would keep growing with this solution but I don't think the number of actions would slow this down too much, especially that generating a unique id that doesn't change might involve storing this ID as well.
Here's the working code: (If you wish to optimize it, please check #BinaryMuse's suggestions in the accepted answer. My MultInput uses a custom Input component that is too large to paste here =) )
var MultiInput = React.createClass({
getInitialState: function() {
value = this.props.value
if (!value || value == '') {
value = [ '' ]
}
return {
value: value
}
},
getDefaultProps: function() {
return {
}
},
add_more: function() {
new_val = this.state.value.concat([])
new_val.push('')
this.setState({ value: new_val })
},
remove_item: function(i,e) {
new_state = this.state.value.concat([])
new_state[i] = undefined
this.setState({ value: new_state })
},
render: function() {
me = this
lines = this.state.value.map( function(e, i) {
if (e == undefined) {
return null
}
return (
<div key={i}>
<input defaultValue={e} />
<button onClick={me.remove_item.bind(null, i)} >X</button>
</div>
)
}).filter( function(e) {
return e != undefined
})
return (
<div>
{lines}
<button onClick={this.add_more}>Add More</button>
</div>
)
}
})