I am learning React, and I been stuck here for a while now, the Middle.js component and table. I am trying to loop over array of objects from the data I am fetching. It is userPools array and display in a table poolProvidersname and then consequently rest of the data.
I am quite confused as to how do I do this?
Here is the the sandbox: https://codesandbox.io/s/naughty-ellis-xrir4?file=/src/components/Table.js
Thank you.
jumping from JS to markup on JSX can bit a bit confusing at first
When iterating an array of objects for display purposes, what you want to do is return a JSX fragment for each item on your array.
So if you have an array that looks like this:
var data = [{ id: 1, name: 'Lucia', age: 20 }, { id: 2, name: 'Karina', age: 21 }, { id: 3, name: 'Maria', age: 22 }];
to display it in your component you would go like this:
const Table = ({ data }) => {
if (!Array.isArray(data)) return null;
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
{
data.map(item => // notice how we jump straight back to JSX here
<tr key={item.id}>
<td>
{item.name}
</td>
<td>
{item.age}
</td>
</tr>)
}
</tbody>
</table>
);
};
Related
I'm making a program that posts train schedules. It's far from done, but I'm trying to implement an "X" button that removes trains from the list right now.
I heard React Components update every time you change their state. So why isn't the table remapping with one less train? The deletion shows up in the console, just not on the table.
function TrainPage(){
const trains =[
{name: "Thomas",
dest: "New York",
first: "05:00",
freq: 30},
{name: "Duncan",
dest: "Boston",
first: "06:00",
freq: 45}
]
const [trainList, setList] = useState(trains);
function remove(event){
console.log(event.target.id)
let train2delete = event.target.id;
trains.splice(train2delete, 1);
console.log(trains)
setList(trains);
console.log(trainList);
//but this doesn't cause the state to rerender. Why?
}
return(
<div>
<table>
<tr>
<th>Train Name</th>
<th>Destination</th>
<th>Frequency</th>
<th>Minutes Away</th>
<th>Next Arrival</th>
</tr>
{trainList.map((train, index) => (
<tr>
<td>{train.name}</td>
<td>{train.dest}</td>
<td>{train.freq}mins</td>
<td></td>
<td></td>
<td><button onClick = {remove} data-toggle='tooltip' data-placement='left' title='delete train' id = {index}>x</button></td>
</tr>
))}
</table>
It would help if you consider the following pointers:
you need to provide the key prop when generating an array of JSX elements(Click here for more information).
When possible avoid using indexes as values for the key prop
whenever you update a piece of state that is an Object, you need to provide a brand new Object (Click here for more information).
function TrainPage(){
const [trainList, setList] = useState([
{name: "Thomas",
dest: "New York",
first: "05:00",
freq: 30},
{name: "Duncan",
dest: "Boston",
first: "06:00",
freq: 45}
]);
function remove(event){
const trainName = event.target.value;
// filter method results in a brand new array;
setList(trainList.filter(entry => entry.name !== trainName)));
}
return (
<div>
<table>
<tr>
<th>Train Name</th>
<th>Destination</th>
<th>Frequency</th>
<th>Minutes Away</th>
<th>Next Arrival</th>
</tr>
{/* using train.name as key value assuming that is unique. if not, please use/generate a unique string */}
{trainList.map((train) => (
<tr key={train.name}>
<td>{train.name}</td>
<td>{train.dest}</td>
<td>{train.freq}mins</td>
<td></td>
<td></td>
<td><button
onClick = {remove}
data-toggle='tooltip'
data-placement='left' title='delete train'
value={train.name}>x</button></td>
</tr>
))}
</table>
</div>
)
}
I have an array which contains multiple objects like this:
const locations = [
{
information: ["Data Analyst", "Uruguay", "Montevideo", "$30,000"],
},
{
information: ["CTO", "UK", "Manchester", "$75,000"],
}
];
And I want to display each array in a new table row containing a <td> with each string.
Currently I have done this:
<tr>
{locations.map((location) => <td>{location.information}</td>)};
</tr>
Which returns each piece of information on a <td>
This is how the data looks at the minute:
And how it's meant to look
For this you have to use Array.map() like:
// To create tha table body by iterating data array
{
locations.map((location, locationIndex) =>
<tr key={locationIndex}>
<td>{location.information[0]}</td>
<td>{location.information[1]}</td>
<td>{location.information[2]}</td>
<td>{location.information[3]}</td>
</tr>
)
}
// In case you don't want to harcode the array index
{
locations.map((location, locationIndex) =>
<tr key={locationIndex}>
location.information.map((loc, locIndex) =>
<td key={locIndex}>{loc}</td>
)
</tr>
)
}
Change the code from,
<tr>
{locations.map((location) => <td>{location.information}</td>)};
</tr>
To:
<table border="1" width="100%" className="component_useRefBox">
<tbody>
{locations.map((location, i) => {
return (
<tr key={i}>
{location.information.map((data, j) => {
return <td key={j}> {data} </td>;
})}
</tr>
);
})}
</tbody>
</table>
To display two records row by row, you need to move {locations.map((location) .... )} above <tr> ... </tr> tag, because while using map method only each row will get iterated and you will get two separate rows..
{locations.map((location, i) => {
return (
<tr>
....
</tr>
)})}
As location.information is of array data, you cannot assign it directly.
You need to iterate using map and display each data like,
{location.information.map((data) => {
return <td> {data} </td>;
})}
how to map subarray in react based on key of parent array?
I have tried mapping by id key of main array to map elements of dish_count array
<TableCell align="left">
{this.state.persons.map((items,name) =>
<div key={this.state.persons.id}>
{(typeof(items.dish_count)=="object")? (<div>
{ items.dish_count.map((subdata) =>
<table>
<td >
{subdata.meal_type}
</td>
</table>
)
}
</div>): (null)}</div>)}
</TableCell>
<TableCell align="left"> {this.state.persons.map((items,name) =>
<div key={this.state.persons.id} >{(typeof(items.dish_count)=="object")? (<div>
{
items.dish_count.map((subdata) =>
<table>
<td >
{subdata.dish_count}
</td>
</table>
)
}
</div>): (null)}</div>)}</TableCell>
i want to map subarray dish_count by key id of parent array .I am able to map but the mapping is multiple and is not exclusive by parent array key.dish_count is the subarray of package array
persons array
"data": [
{
"id": 1,
"name": "Gold",
"dish_count": [
{
"dish_count": 4,
"meal_type": "Starters"
},
{
"dish_count": 4,
"meal_type": "Main Course"
},
{
"dish_count": 4,
"meal_type": "Dessert"
},
{
"dish_count": 4,
"meal_type": "Lunch"
}
]
},
{
"id": 2,
"name": "Basic",
"dish_count": [
{
"dish_count": 2,
"meal_type": "Starters"
},
{
"dish_count": 2,
"meal_type": "Main Course"
},
{
"dish_count": 2,
"meal_type": "Dessert"
},
{
"dish_count": 2,
"meal_type": "Lunch"
}
]
}
]
I want
Meal Type No of Dishes
Gold Starters 4
Main Course 4
Desert 4
Lunch 4
Basic Starters 2
Main Course 2
Desert 2
Lunch 2
You have a few problems in your code:
this.state({persons:''});: This will set the initial persons to be an empty string and will fail with map.
console.log('package',this.state.persons): setState is async and the console.log will not print the desired state but the previous state. Use the callback of setState as second parameter to access the new state:
this.setState({persons:res.data.data.data}, () => console.log('package',this.state.persons) );
this.state.persons.map((items, name) =>: The map function will provide different parameters: the first is the person and the second is the index of that person within the array.
div key = {this.state.persons.id}: since persons is an array, the key will be undefined. If you use the map function correctly, you can use person.id.
When you fixed these problems, the code should work as expected.
To only show the expected dishes per person and not to print the duplication you have to write it like this:
class Table extends React.Component {
render() {
return <table>
<thead>
<tr>
<td>ID</td>
<td>Name</td>
<td>Meal Type</td>
<td>Number of Dishes</td>
</tr>
</thead>
<tbody>
{this.state.persons.map(person =>
<tr>
<td>{person.id}</td>
<td>{person.name}</td>
<td>
<table>
<tbody>
{person.dish_count.map(dish => <tr>
<td>{dish.meal_type}</td>
</tr>
)}
</tbody>
</table>
</td>
<td>
<table>
<tbody>
{person.dish_count.map(dish => <tr >
<td>{dish.dish_count}</td>
</tr>
)}
</tbody>
</table>
</td>
</tr>
)
}
</tbody>
</table >
}
}
By not iterating over the person for each sub-table, you can remove the duplicated data.
codepen
Hope this helps.
I am having some issues trying to populate a table with two arrays. I am trying to achieve the following output:
I have an array called students and within each student object, I have a courses array. I am trying to populate my table using ng-repeat to show all courses against each student.
Not all data is given within each course, meaning that if a student hasn't started a course then it won't appear in the courses array. Also, if a student hasn't finished a course there will be no date completed given.
So my array looks like so:
$scope.students = [
{
Id: 1,
Name: 'Joe Blogs',
Courses: [
{
Title: 'Course 1',
Grade: 87,
Completed: true,
DateCompleted: '2018-01-01'
},
{
Title: 'Course 2',
Grade: 2,
Completed: false
}
]
},
{
Id: 2,
Name: 'Peter Smith',
Courses: [
{
Title: 'Course 1',
Grade: 100,
Completed: true,
DateCompleted: '2018-01-01'
},
{
Title: 'Course 2',
Grade: 95,
Completed: true,
DateCompleted: '2018-01-01'
},
{
Title: 'Course 3',
Grade: 10,
Completed: false
}
]
},
{
Id: 3,
Name: 'Joanne Someone',
Courses: [
{
Title: 'Course 3',
Grade: 55,
Completed: false,
}
]
}
]
So far my code snippet looks like this:
<table class="table table-hover" width="100%">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Course</th>
<th>Grade</th>
<th>Completed</th>
<th>Date Completed</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="student in students">
<td>{{ student.Id }}</td>
<td>{{ student.Name }}</td>
<td>
<!-- Can't figure this bit out -->
</td>
</tr>
</tbody>
</table>
But I am stuck on how to get multiple courses to show against each student. Also I have found that by using ng-repeat, if a value isn't in the object (e.g. Date Completed) then that field will not show and pushes everything up and out of alignment.
Lastly, I will mention that I am using angular-datatables however, I am just trying to work out the concept, then I can apply this to datatables later.
You'll have to use rowspan for that purpose. And the value of that for particular Id should be of length of Courses. Then, you'll need to have ng-repeat on tbody to repeat tbody section & have ng-repeat inside on tr for repeating Courses arrays of each student. So, your table view code can be:
<table>
<thead>
<tr>
<td>Id</td>
<td>Name</td>
<td>Course</td>
<td>Grade</td>
<td>Completed</td>
<td>Date Completed</td>
</tr>
</thead>
<tbody ng-repeat="x in students">
<tr>
<td rowspan="{{x.Courses.length}}"><span>{{ x.Id }}</span></td>
<td rowspan="{{x.Courses.length}}"><span>{{ x.Name }}</span></td>
<td><span>{{x.Courses[0].Title}}</span></td>
<td><span>{{x.Courses[0].Grade}}</span></td>
<td><span>{{x.Courses[0].Completed}}</span></td>
<td><span>{{x.Courses[0].DateCompleted}}</span></td>
</tr>
<tr ng-repeat="item in x.Courses" ng-if="$index > 0">
<td><span>{{item.Title}}</span></td>
<td><span>{{item.Grade}}</span></td>
<td><span>{{item.Completed}}</span></td>
<td><span>{{item.DateCompleted}}</span></td>
</tr>
</tbody>
</table>
To avoid breaking of table code if some values are not available, just have it inside span element so it'll still load td with no value inside it.
Plunker Example
In a json feed (below) I have two arrays, "rent" and "buy" I wish to join and display in an html table but I'm not sure where to do it.
The feed looks like this...
"store": {
"rent": [
{ "title": "Lord of the ring masters", "cost": 2.99 }
],
"buy": [
{ "title": "Fast and Furious 14", "cost": 5.99 },
{ "title": "Shogun Assassin", "cost": 2.99 }
],
"total": 30.20
}
And the render function in my view, which will display one of the above correctly looks like this
render: function(){
var createRow = function(rowItem, i){
return (
<tr key={i}>
<td>{rowItem.name}</td>
<td>{rowItem.cost}</td>
</tr>
);
};
return (
<div>
<h1>Package</h1>
<table className="table">
<thead>
<th>Package</th>
<th>Name</th>
<th>Cost</th>
</thead>
<tbody>
{this.props.packageList.rent.map(createRow, this)}
</tbody>
</table>
Total: {this.props.packageList.total}
</div>
);
}
Could any one tell me how I would alter the above to join the arrays and present the data like this...
**Rent** Lord of the ring masters £2.99
**Buy** Fast and Furious 14 £5.99
**Buy** Shogun Assassin £2.99
Rather than having your map function called within the render, create another method on your object that returns an array of rows. So your new component looks like:
var myClass = React.createClass({
renderRows: function() {
var rows = [];
this.props.packageList.rent.forEach(function(rowItem, i) {
rows.push(
<tr key={i}>
<td>rent</td>
<td>{rowItem.name}</td>
<td>{rowItem.cost}</td>
</tr>
);
});
this.props.packageList.buy.forEach(function(rowItem, i) {
rows.push(
<tr key={i}>
<td>buy</td>
<td>{rowItem.name}</td>
<td>{rowItem.cost}</td>
</tr>
);
});
return rows;
},
render: function(){
return (
<div>
<h1>Package</h1>
<table className="table">
<thead>
<th>Package</th>
<th>Name</th>
<th>Cost</th>
</thead>
<tbody>
{this.renderRows()}
</tbody>
</table>
Total: {this.props.packageList.total}
</div>
);
}
})
Alternatively, you could combine the data ahead of time and just do one loop over the entire thing. Finally, you probably don't want to define new function in your render function, rather, as I suggested, create a new method on the component. Also, I haven't tested the above, so be sure to check for errors.