Error when looping to an array in React JS - javascript

This is my code in "App.js" as the array I want to loop from, I wanted to loop from the array below and output the contents of it in the browser with a table.
This is my return
return (
<div className="container">
<h1> Expenses </h1>
<table className="table table-bordered">
<tr>
<th>Title</th>
<th>Amount</th>
<th>Date</th>
</tr>
{expenses.map((expense, index) => (
<tr data-index={index}>
<td>{expense.title}</td>
<td>{expense.amount}</td>
<td>{expense.date}</td>
</tr>
))}
</table>
</div>
);
This is the code from my component.
import './ExpenseItem.css';
function ExpenseItem(props) {
return (
<div className="expense-item" >
<div>{props.date.toISOString()}</div>
<div className="expense-item__description">
<h2>{props.title}</h2>
<div className="expense-item__price">{props.amount}</div>
</div>
</div>
)
}
export default ExpenseItem
This is the error that I am getting.

You need to convert dates to strings. In your code, you pass Date Objects to HTML. The data format should be String. You can see formatting options for dates on MDN.
<td>{expense.date.toString()}</td>

You're trying to output a Date object:
<td>{expense.date}</td>
Which, as the error states, is an object and not a simple value. So React has no way of knowing how to display it.
You can define the format and output that. For example:
<td>{expense.date.toLocaleDateString()}</td>
There are a variety of functions built-in to the Date object to format it, or you can combine multiple values from it into a custom format, or use a date formatting/parsing library, etc.

return (
<div className="container">
<h1> Expenses </h1>
<table className="table table-bordered">
<tr>
<th>Title</th>
<th>Amount</th>
<th>Date</th>
</tr>
{expenses.map((expense, index) => (
<tr data-index={index}>
<td>{expense.title}</td>
<td>{expense.amount}</td>
<td>{expense.date.toISOString()}</td>
</tr>
))}
</table>
</div>
);
Date object is not valid react child.

I think in App.js when you loop through the expenses array, you want to return the ExpenseItem component, passing it the current expense item as a prop, like this:
{expenses.map((expense, index) => (
<ExpenseItem item={expense} />
))}
And in ExpenseItem, since props is an object, you should change props.title to props.item.title etc.
Hope this helps!

Related

How do I dynamically generate table rows without jquery?

Currently, I have a table class as follows:
import React from "react";
import "./Table.css";
export default function Table({theadData, tbodyData}) {
return (
<>
<table>
<tr>
<th></th>
<th>2017</th>
</tr>
{Array.from(theadData).forEach(heading => {
<tr>
<td class="largeHeader" key={heading}>{heading}</td>
<td class="primaryCell">{tbodyData[heading].value}</td>
</tr>;
})}
</table>
</>
);
}
When I add console.log(heading) or console.log(tbodyData[heading].value) within the loop, I can see that they give the expected values. However, none of the rows are added on. Why is that and how can I solve this problem? (I'd prefer to avoid jquery and libraries of that nature if possible, but am open to ideas.)
There are several mistakes you made:
change forEach to map
replace {} with (), or add return before <tr>
put key on the root element which is <tr>
{Array.from(theadData).map(heading => (
<tr key={heading}>
<td className="largeHeader">{heading}</td>
<td className="primaryCell">{tbodyData[heading].value}</td>
</tr>
))}

Handling array length in React

I have an array of results I'm wanting to render in a table:
const ScanOutput = () => (
<div class="results">
<h1>
<b>Scan Results</b>
</h1>
<h3>{results.length} results returned</h3>
<table class="styled-table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
</tr>
</thead>
<tbody>
{results.map((result) => (
<tr>
<td>{result.id}</td>
<td>{result.name}</td>
</tr>
))}
</tbody>
</table>
</div>
);
This works, but I'd like to add some extra features - for example, if no results are returned (e.g. results.length == 0) I'd like to render "No results found" instead of an empty table. Furthermore, I'd also like to make it so that with this section here:
<h3>{results.length} results returned</h3>
If only 1 result is returned it renders this instead:
<h3>{results.length} result returned</h3>
What's the cleanest way to do this in React?
You can render conditionally.
<div class="results">
{result?.length > 0 ?
<>
<h1>
<b>Scan Results</b>
</h1>
<h3>{results.length} results returned</h3>
<table class="styled-table">
{...}
</table>
</>
:
<h1>No results found</h1>
}
</div>
You can create a new component for the results table
const ResultsTable = ({ results }) => {
if (results?.length === 0) return <h3>No results found</h3>;
if (results?.length === 1) return ...;
return (
<table class="styled-table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
</tr>
</thead>
<tbody>
{results.map((result) => (
<tr>
<td>{result.id}</td>
<td>{result.name}</td>
</tr>
))}
</tbody>
</table>
)
}

Trying to get data of the row clicked of the table react js

I am new to react,and making app where I want to get data of the row being clicked on a table,I don't whether this approach is good or not need suggestions
So far i have used onClick listener but when I click on a table , it gives me [object,object] in console.log(contract),I have also tried to to use loop to view data in it but it gives me [object,object],here's my below code:
<table id="mytable" cellSpacing="0" width="100%">
<thead>
<tr>
<th>CarName</th>
<th>DriverName</th>
<th>Date</th>
<th>Pickup</th>
</tr>
</thead>
<tbody>
{
this.state.contracts.map((contract,index)=>{
return(
<tr key={index} data-item={contract} onClick={this.contractdetails}>
<td>{contract.car} </td>
<td>{contract.driver}</td>
<td>{contract.date}</td>
<td>{contract.pickup}</td>
</tr>
)})
}
</tbody>
</table>
onClickfunction
contractdetails=(e)=>{
const contract=e.currentTarget.getAttribute('data-item');
console.log(contract)
};
Use JSON.stringify while setting the data-item
<tr key={index} data-item={JSON.stringify(contract)} onClick={this.contractdetails}>
And use JSON.parse() while accessing it.
contractdetails=(e)=>{
const contract= JSON.parse(e.currentTarget.getAttribute('data-item'));
console.log(contract)
};
A better way is to just set index as data-index and you can access it from the state
this.state.contracts.map((contract,index)=>{
return(
<tr key={index} data-index={index} onClick={this.contractdetails}>
...
...
}
You can access it like below
contractdetails=(e)=>{
let index = +e.currentTarget.getAttribute('data-index')
console.log(this.state.contracts[index]);
};

Invariant Violation when trying to render array of table row elements

I am using React and I'm trying to render a table that will be dynamic in the number of rows depending on the result size.
However I am getting the following error:
Unhandled Rejection Invariant Violation: Objects are not valid as
React child (found: objects with keys {name}). If you meant to render
a collection of children, use an array instead or wrap the object
using createFragment(object).
I have the following code and loop in the render function :
let result = this.props.result.exactMatches;
var rows = [];
for(var i = 0; i< result.length; i++) {
rows.push(
<tr>
<td width="50%"> result[i].name </td>
<td width="50%"> result[i].position </td>
</tr>
);
}
Then in the return jsx element I have the following div element:
<div>
<table style={{width:'100%'}}>
<tbody>
<tr>
<th width="50%"> Name </th>
<th width="50%"> Position </th>
</tr>
{rows}
</tbody>
</table>
</div>
Thanks in advance!
The error log is pretty self explanatory but lets go through it carefully.
Objects are not valid as React child
That pretty much means that React does not know to render an array of React components in the form you are providing it.
If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object)
I would suggest going through createFragment documentation. The issue described is similar yours.
The React rendering pipeline uses a couple of heuristics to improve performance. One of them requires the developer to distinguish adjacent elements of the same type (e.g. <tr />) using a unique key property in each one of them.
Onto the solution itself, I would suggest embracing a more functional programming approach. map() is a method used on arrays. Think of it as turn this array of cats into an array of dogs but let me show you how.
In your case, you want to turn an array of matches called exactMatches into rows of a table, and thats what we are going to do.
<div>
<table style={{width:'100%'}}>
<tbody>
<tr>
<th width="50%"> Name </th>
<th width="50%"> Position </th>
</tr>
this.props.result.exactMatches.map((match, index) => (
<tr key={`row-${match.name}-${index}`}>
<td width="50%">match.name</td>
<td width="50%">match.position</td>
</tr>
))
</tbody>
</table>
</div>
This is the correct way to do it.
let result = this.props.result.exactMatches;
var rows = [];
for(var i = 0; i< result.length; i++) {
rows.push(
{name: result[i].name , position:result[i].position}
);
}
and render it like this.
<div>
<table style={{width:'100%'}}>
<tbody>
<tr>
<th width="50%"> Name </th>
<th width="50%"> Position </th>
</tr>
{rows.map((rowData, index) =>{
return <tr key=`results-row-${rowData.name}-${index}`>
<td width="50%"> {rowData.name} </td>
<td width="50%"> {rowData.position} </td>
</tr>
})}
</tbody>
</table>
</div>
This is a correct way:
<div>
<table style={{width:'100%'}}>
<tbody>
<tr>
<th width="50%"> Name </th>
<th width="50%"> Position </th>
</tr>
{this.props.exactMatches.map((item, i) => <tr key={i}>
<td width="50%"> {item.name }</td>
<td width="50%"> {item.position} </td>
</tr>)}
</tbody>
</table>
</div>
Pay attention for keys, if you have something unique value in each entity you can use it.

Map through an array that creates a horizontal table

I am trying to map through an array and dynamically create a horizontal table, much like the image below here.
I am mapping through an array like so,
const glanceGames = this.state.gameData.map(game => {
return <GameTable
key={game.id}
home_team_name={game.home_name_abbrev}
away_team_name={game.away_name_abbrev}
home_score={game.linescore.r.home}
away_score={game.linescore.r.away}
status={game.status.status}
/>
})
and then using that data to populate the following component.
const GameTable = (props) => {
return (
<div>
<table>
<thead>
<tr>
<th>{props.status}</th>
</tr>
</thead>
<tbody>
<tr>
<td>{props.home_team_name}</td>
<td>{props.home_score}</td>
</tr>
<tr>
<td>{props.away_team_name}</td>
<td>{props.away_score}</td>
</tr>
</tbody>
</table>
</div>
)
}
However, the output is a vertical table rather than a horizontal one. I feel like this should be easy, yet I keep running into issues. Anu suggestions would be helpful! I am using React.
I don't think this is nothing to do with react, we can just do it with css:
...
render(){
<div style={{display: 'flex'}}>
{glanceGames}
</div>
}

Categories