Attempting to generate a table using ReactJS and within this table I want to have a unique key to identify each row (as recommended by ReactJS). The following code runs and generates the table I desire except with the following error:
Warning: 'Each child in an array or iterator should have a unique "key" prop'
Looking at my generated HTML the 'key' field indeed does not exist. I've also tried key={index} but it doesn't work either
export class Tablecontent extends Component{
constructor(props){
super(props);
this.state={
fileinfo:'null'
}
}
render(){
//const fileinfo = this.props.rows;
const fileinfo = ['myFile1.doc','myFile2.doc'];
var row='';
console.log("FILEINFO:"+fileinfo);
if(fileinfo=='null')
{
row ='';
}
else {
row = fileinfo.map((data,index) =>
<tr>
<td key="filename_{index}">{data}</td>
<td key="date_{index}">2/12/2017</td>
<td key="size_{index}">2.1 MB</td>
</tr>
);
}
return(
<div>
<table>
<tbody>
<tr>
<th>Name</th>
<th>Date Created</th>
<th>Size</th>
</tr>{row}</tbody>
</table>
</div>
);
}
}
Resulting HTML:
<table>
<tbody>
<tr><th>Name</th><th>Date Created</th><th>Size</th></tr>
<tr><td>myFile1.doc</td><td>2/12/2017</td><td>2.1 MB</td></tr>
<tr><td>myFile2.doc</td><td>2/12/2017</td><td>2.1 MB</td>
</tr>
</tbody>
</table>
It wants to see the key on the outer element returned in the map function as below. The key is for React's internal use and won't display in the html.
row = fileinfo.map((data,index) =>
<tr key={index}>
<td key="filename_{index}">{data}</td>
<td key="date_{index}">2/12/2017</td>
<td key="size_{index}">2.1 MB</td>
</tr>
);
Related
I'm a newbie in development, so any help is welcome :)
Here's my Problem:
I'm using next.js - this might be important, because f.ex. document.createElement seem only work with useEffect.
I created a text area, where users add words separated with a comma (f.ex. "Nike, Adidas, New Balance"). I want to rewrite the existing code below, so that:
table is only visible/appears, when a user adds something in the inputbox
each word from the inputbox (f.ex. Nike, Adidas) creates a new row in the table (in table head "Brand").
By now, I was trying to use the .split and .forEach method to create a new Element(table). But I just can't make it work - maybe it's just not the right solution. Any help is welcome!
function Analyzer() {
const [brand, setBrand] = React.useState('');
const handleChange = (event) => {
setBrand(event.target.value.split(','))};
return(
<div>
<textarea type="text"
placeholder="Example:
Nike, Adidas, New Balance ..."
onChange={handleChange}></textarea>
<table className={styles.table}>
<thead>
<tr>
<th>No.</th>
<th>Brand</th>
<th>Also known as</th>
<th>Avg. price</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>{brand}</td>
<td></td>
<td><input type= "number"></input>%</td>
</tr>
</tbody>
</table>
</div>);
}
Try something like the following. What I did was extract the Table component out and put any conditions for its rendering within it. You can even create a separate file for this component and pass any relevant props to it.
Secondly, I am putting the list of brands into state as brands within the handleChange function. This state variable is then iterated over using .map to render each row in the table.
function Analyzer() {
const [brands, setBrands] = React.useState([]);
const handleChange = ({ target }) => {
const { value } = target;
setBrands(value.split(','));
};
const Table = () => {
if (!brands || brands.length === 0) return null;
return (
<table className={styles.table}>
<thead>
<tr>
<th>No.</th>
<th>Brand</th>
<th>Also known as</th>
<th>Avg. price</th>
</tr>
</thead>
<tbody>
{brands.map((brand) => (
<tr>
<td>1</td>
<td>{brand}</td>
<td></td>
<td><input type= "number"></input>%</td>
</tr>
)}
</tbody>
</table>
);
};
return(
<div>
<input
onChange={handleChange}
placeholder="Example: Nike, Adidas, New Balance ..."
type="text"
value={brands}
/>
<Table />
</div>
);
};
I have data being sent from my backend to my frontend and then the data is parsed and displayed into a table. However, at it's max amount of elements in the array, there are too many rows in the table and it exceeds the area that I need the table to be in. So a carousel/slide animation would be the best bet and I want to be able to display elements 4 rows at a time and then invoke the slide animation the the next 4. I'm not quite sure how to do this in the current iteration of my function that sets the table data.
Can someone point me in the right direction?
function FormTable(props){
/**
* props = allStations
*/
console.log(props.data)
return(
<table className="form__table">
<div className="form__table-parentDiv">
<thead>
<tr>
<th className="form__table-header">Departing Station</th>
<th className="form__table-header">Arriving Station</th>
<th colSpan={2} className="form__table-header">Departure Time</th>
</tr>
</thead>
<tbody>
{
props.data.map( (stationIndex) => {
// console.log(stationIndex);
let cells = [];
for(let i = 1; i < stationIndex.length; i++){
cells.push(
<React.Fragment>
<td className="form__table-stations"> {stationIndex[i].station} </td>
<td className="form__table-stations"> {stationIndex[i].destination} </td>
<td className="form__table-arrivals"> {stationIndex[i].firstArrival} </td>
<td className="form__table-arrivals"> {stationIndex[i].secondArrival} </td>
</React.Fragment>
);
}
return <tr className="form__table-row" >{ cells }</tr>
})
}
</tbody>
</div>
</table>
)
}
I'm currently working on a table in React and need to have a select status on a column when clicking the top cell and being able to toggle that between the columns.
At this stage I roughly put together something like this:
const types = {
one: 'one',
two: 'two',
}
class Test extends Component {
constructor(props) {
super(props)
this.onOneClick = this.onClick.bind(this, types.one)
this.onTwoClick = this.onClick.bind(this, types.two)
}
onClick = column => {
this.props.onColumnChange(column)
}
render() {
const { column } = this.props
const classOne = classnames(styles.one, column === types.one ? styles.active : null)
const classTwo = classnames(styles.two, column === types.two ? styles.active : null)
return (
<section className={styles.columnSelection}>
<table className={styles.columns}>
<thead>
<tr>
<th>Select column</th>
<td className={classOne} onClick={this.onOneClick}>one</td>
<td className={classTwo} onClick={this.onTwoClick}>two</td>
</tr>
</thead>
<tbody>
<tr>
<th></th>
<td className={classOne}>one</td>
<td className={classTwo}>two</td>
</tr>
<tr>
<th></th>
<td className={classOne}>one</td>
<td className={classTwo}>two</td>
</tr>
</tbody>
</table>
</section>
)
}
}
Test.propTypes = {
column: PropTypes.oneOf(Object.values(types)),
onColumnChange: PropTypes.func.isRequired,
}
I think this will probably achieve what I want but wondering if there are better ways to do that? also I don't think there is any nth-child selector like in jQuery to help me. There are few different reasons on why I need the table, so I'm not looking to change the HTML structure.
Create a css style for '.active' for highlighting the selected column and change the above code like,
class Test extends Component {
constructor(props) {
super(props)
state={
activeColumn : 0
}
}
render() {
return (
<section className={styles.columnSelection}>
<table className={styles.columns}>
<thead>
<tr>
<th>Select column</th>
<td className={this.state.activeColumn===1 ? "active":"" } onClick={()=>this.setState({activeColumn:1})}>one</td>
<td className={this.state.activeColumn===2 ? "active":"" } onClick={()=>this.setState({activeColumn:2})}>two</td>
</tr>
</thead>
<tbody>
<tr>
<th></th>
<td className={this.state.activeColumn===1 ? "active":"" }>one</td>
<td className={this.state.activeColumn===2 ? "active":"" }>two</td>
</tr>
<tr>
<th></th>
<td className={this.state.activeColumn===1 ? "active":"" }>one</td>
<td className={this.state.activeColumn===2 ? "active":"" }>two</td>
</tr>
</tbody>
</table>
</section>
)
}
}
You are probably doing it wrong. In React you need to use state in order to maintain the component state. And on click or any other event, you actually updated that component state value. The state is usually initialized in constructor like this.state={columns: 'one'}
Then in render() method, you are using that state using this.state.columns to check and render or output any expression or you then output any JSX/HTML. You can use ternary operation e.g. (this.state.columns=='two' ? 'It is two column' : 'its one column).
Inside your onclick or any event handler you update the state of the component using this.setState().
For more details check this guide: https://medium.freecodecamp.org/learn-react-js-in-5-minutes-526472d292f4
I would really recommend that you first go through its documentation and examples before doing real codding.
I'm new to using ReactJS and react-router. I want a clickable table row and something like the following setup:
<Link to=“#”>
<tr>
<td>{this.props.whatever1}</td>
<td>{this.props.whatever2}</td>
<td>{this.props.whatever3}</td>
</tr>
</Link>
but I know you can't put <a> tags between the <tbody> and <tr> tags. How else can I accomplish this?
PS: I prefer not to use jQuery if possible.
onClick works, but sometimes you need an actual <a> tag for various reasons:
Accessibility
Progressive enhancement (if script is throwing an error, links still work)
Ability to open a link in new tab
Ability to copy the link
Here's an example of a Td component that accepts to prop:
import React from 'react';
import { Link } from 'react-router-dom';
export default function Td({ children, to }) {
// Conditionally wrapping content into a link
const ContentTag = to ? Link : 'div';
return (
<td>
<ContentTag to={to}>{children}</ContentTag>
</td>
);
}
Then use the component like this:
const users = this.props.users.map((user) =>
<tr key={user.id}>
<Td to={`/users/${user.id}/edit`}>{user.name}</Td>
<Td to={`/users/${user.id}/edit`}>{user.email}</Td>
<Td to={`/users/${user.id}/edit`}>{user.username}</Td>
</tr>
);
Yes, you'll have to pass to prop multiple times, but at the same you have more control over the clickable areas and you may have other interactive elements in the table, like checkboxes.
Why don't you just use onClick?
var ReactTable = React.createClass({
handleClick: function(e) {
this.router.transitionTo('index');
},
render: function() {
return(
<div>
<table>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Full Detail</th>
</tr>
</thead>
<tbody>
<tr onClick={this.handleClick.bind(this)}>
<td>{user.name}</td>
<td>{user.age}</td>
<td>{details}</td>
</tr>
</tbody>
</table>
</div>
);
}
});
This answers is based on #Igor Barbasin suggestion. This will add the link to the whole row instead of just the content and we also don't need to wrap all the individual 'td' element with 'Link'.
.table-row {
display: table-row
}
export default function Table() {
return (
<table>
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
<tbody>
{/* treats the link element as a table row element */}
<Link className="table-row">
<td>Noname</td>
</Link>
</tbody>
</table>
)
}
You can use useHistory() hook:
Declare:
const history = useHistory();
and use it with <tr> or <td> tag:
<tr onClick={() => history.push("/yoururl")}>
<td>{this.props.whatever1}</td>
<td>{this.props.whatever2}</td>
<td>{this.props.whatever3}</td>
</tr>
{shipment.assets.map((i, index) => (
<tr
style={{ cursor: "pointer" }}
onClick={(e) => e.preventDefault()}
>
<td>{index + 1}</td>
<td>{i._id}</td>
<td>{i.status}</td>
<td></td>
</tr>
))}
Every time the toggle is clicked, all payments are getting replaced with new payments. My problem is how to maintain the payments of a particular index of every click and show at respective index. please help me out
here is my html
<tbody data-ng-repeat="invoice in relatedInvoices>
<tr>
<td class="td-bottom-border">
{{invoice.PayableCurrencyCode}} {{invoice.PayablePaidAmount | number: 2}}<br />
<small>
<a data-ng-click="isOpenPayablePayments[$index] = !isOpenPayablePayments[$index]; togglePayablePayments(invoice.PayableInvoiceId)">Paid</a>
</small>
</td>
</tr>
<tr data-ng-show="isOpenPayablePayments[$index]">
<td>
<table>
<thead>
<tr>
<th>Transaction Id</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="payment in payablePayments">
<td>{{payment.TransactionId}}</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
Here is my javascript
var getPayments = function (invoiceId) {
paymentService.getPayments(invoiceId).then(function (paymentsResponse) {
return paymentsResponse.data;
});
};
$scope.togglePayablePayments = function(invoiceId) {
$scope.payablePayments = getPayments(invoiceId);
};
If I understood correctly, you want to have "payablePayments" for every invoice.
This is working: http://plnkr.co/edit/cj3jxZ?p=info
Try something like
// init at beginning
$scope.payablePayments = [];
$scope.togglePayablePayments = function(invoiceId) {
$scope.payablePayments[invoiceId] = getPayments(invoiceId);
};
and then
<tr data-ng-repeat="payment in payablePayments[invoice.PayableInvoiceId]">
Otherwise you overwrite the object for the preceding invoice.