Reactjs - Splitting table row into two rows - fields are not aligning properly - reactjs

Am trying to split table row into two rows. So, i writting two tr tag in my render() method. Am able to split the rows but, its alignment is not coming properly.
All the fields are rendering in the first column itself. How can i split the table properly.
Am using Reactjs V 15.6.1
class Details extends React.Component {
constructor(props) {
super(props);
this.state = { item: props.item };
}
render() {
return (
<table className="table table-bordered" style={{fontsize: '8'}}>
<thead>
<tr>
<th>Line Number</th>
<th>Product Code</th>
<th>Description</th>
<th>Quantity</th>
<th>Unit Price</th>
</tr>
</thead>
<DetailsList items={ this.state.item.order_items } />
</table>
);
}
}
class DetailsList extends React.Component {
constructor(props) {
super(props);
this.state = { };
}
render() {
return (<tbody>{ this.props.items.map((item) => <DetailsItem key={
item.line_id } item={ item } />) }
</tbody>);
}
}
class DetailsItem extends React.Component {
constructor(props) {
super(props);
this.state = { item: props.item };
}
render() {
return (<tbody>
<tr>
<td><input name="line_number" type="text"
value={ this.state.item.line_number } /> </td>
<td><input name="product_code" type="text"
value={ this.state.item.product_code } /></td>
<td><input name="product_description" type="text"
value={ this.state.item.product_description } /> </td>
</tr>
<tr>
<td>{ this.state.item.product_quantity } </td>
<td>{ this.state.item.unit_net_price } </td>
</tr>
</tbody>
);
}
}
Please find my output screenshot below. All fields are rendered in the same column. Its happening while am adding second table row tag.
Also, if remove <tbody> tag from the return() method also its throwing error Adjacent JSX elements must be wrapped in an enclosing tag.

ReactJS V 15.x.x only allow one components within render() method. Its not possible to return multiple <tr> tag. This has been addressed from ReactJS 16+ https://reactjs.org/docs/jsx-in-depth.html.

Related

Each child in a list should have a unique "key" prop Error in React

Hi I am currently working with Spotify API with Spring Boot as the backend and React for the front end.
I was able to retrieve a user's favorite artist using the API in a json format but as I am not familiar with React, I am currently having problems displaying the data.
The json data that I have retrieved looks something like this.
[{"externalUrls":{"externalUrls":{"spotify":"https://open.spotify.com/artist/3dz0NnIZhtKKeXZxLOxCam"}},"followers":{"href":null,"total":929384},"genres":["complextro","edm","progressive electro house"],"href":"https://api.spotify.com/v1/artists/3dz0NnIZhtKKeXZxLOxCam","id":"3dz0NnIZhtKKeXZxLOxCam","images":[{"height":640,"url":"https://i.scdn.co/image/ab6761610000e5eb1804f56bdcb9322c5f3f8f21","width":640},{"height":320,"url":"https://i.scdn.co/image/ab676161000051741804f56bdcb9322c5f3f8f21","width":320},{"height":160,"url":"https://i.scdn.co/image/ab6761610000f1781804f56bdcb9322c5f3f8f21","width":160}],"name":"Porter Robinson","popularity":68,"type":"ARTIST","uri":"spotify:artist:3dz0NnIZhtKKeXZxLOxCam"},{"externalUrls":{"externalUrls":{"spotify":"https://open.spotify.com/artist/6M2wZ9GZgrQXHCFfjv46we"}},"followers":{"href":null,"total":27618208},"genres":["dance pop","pop","uk pop"],"href":"https://api.spotify.com/v1/artists/6M2wZ9GZgrQXHCFfjv46we","id":"6M2wZ9GZgrQXHCFfjv46we","images":[{"height":640,"url":"https://i.scdn.co/image/ab6761610000e5ebd42a27db3286b58553da8858","width":640},{"height":320,"url":"https://i.scdn.co/image/ab67616100005174d42a27db3286b58553da8858","width":320},{"height":160,"url":"https://i.scdn.co/image/ab6761610000f178d42a27db3286b58553da8858","width":160}],"name":"Dua Lipa","popularity":94,"type":"ARTIST","uri":"spotify:artist:6M2wZ9GZgrQXHCFfjv46we"}]
I am hoping to display this with React but seem to run into an error that reads
"Warning: Each child in a list should have a unique "key" prop."
My 'ListArtistComponent.jsx' file in my React application looks something like this.
class ListArtistComponent extends Component{
constructor(props) {
super(props);
this.state = {
artist: []
}
}
componentDidMount(){
ArtistService.getArtist().then((res) => {
this.setState({ artist: Array.from(res.data)});
});
}
render() {
return(
<div>
<h2 className="text-center">Artist List</h2>
<div className="row">
<table className="table table-striped table-bordered">
<thead>
<tr>
<th>Artist Name</th>
<th>Artist Popularity</th>
</tr>
</thead>
<tbody>
{
this.state.artist.map(
artist =>
<tr key = {artist.id}>
<td> {artist.name} </td>
<td> {artist.popularity} </td>
</tr>
)
}
</tbody>
</table>
</div>
</div>
)
}
}
export default ListArtistComponent;
And my ArtistService looks something like this
class ArtistService {
getArtist(){
return axios.get("http://localhost:8080/api/topartist")
}
}
export default new ArtistService()
I am guessing that a problem occurred when setting the 'id'(in the given json "3dz0NnIZhtKKeXZxLOxCam" and "6M2wZ9GZgrQXHCFfjv46we" respectively) value as the key, it would be appreciated if I could know what seems to be causing the problem and a solution for it.
Thank you in advance!!
I don't see any problem.
But if we use array index for the unique key.
can you try this?
class ListArtistComponent extends Component{
constructor(props) {
super(props);
this.state = {
artist: []
}
}
componentDidMount(){
ArtistService.getArtist().then((res) => {
this.setState({ artist: Array.from(res.data)});
});
}
render() {
return(
<div>
<h2 className="text-center">Artist List</h2>
<div className="row">
<table className="table table-striped table-bordered">
<thead>
<tr>
<th>Artist Name</th>
<th>Artist Popularity</th>
</tr>
</thead>
<tbody>
{
this.state.artist.map(
(artist,index) =>
<tr key = {index}>
<td> {artist.name} </td>
<td> {artist.popularity} </td>
</tr>
)
}
</tbody>
</table>
</div>
</div>
)
}

How do I access a table <td> via refs instead of id in React.js

How do I access a refs instead of an id defined in a td of a table? For example, in my React component, I have a td with id="myId". Please see code below:
`componentDidUpdate() {
if (document.getElementById('myId')) {
// I want to use refs instead of id here. How do I do that?
document.getElementById('myId').click();
}
}`
`return (
<div>
<table width="90%" height="90%">
<tbody>
<tr>
<td>{this.props.something}</td>
<td id="myId" onClick={() => this.props.someFunctionToRun()}>Click me
</td>
</tr>
</tbody>
</table>
</div>
);`
Simply use ref prop on td. Pass a internal argument as reference and assign to variable you want (i.e. this.nameInput)
To use it as DOM element, you must define it in constructor using .createRef() method.
Like :
constructor(props) {
super(props);
this.nameInput = React.createRef(); // define ref
}
componentDidUpdate() {
if (this.nameInput) {
// Use DOM element's native method here
this.nameInput.click();
}
}
return (
<div>
<table width="90%" height="90%">
<tbody>
<tr>
<td>{this.props.something}</td>
<td ref={input => {
this.nameInput = input;
}} onClick={() => this.props.someFunctionToRun()}>Click me
</td>
</tr>
</tbody>
</table>
</div>
);

How to set State when value is derived from Array in React App

I have a react App, which renders a table from an array in the state.
I am trying to have an "EDIT MODE" which transforms some fields into textbox's so that I can update each row in the table.
I'm not sure how I can handle the onChange event when the value is derived from an element inside an array in the state.
here is my code, I have explained the problem in the comments:
class AddProjectType extends Component {
constructor(props) {
super(props);
this.state = {
editMode: false
}
this.changeEditMode = this.changeEditMode.bind(this); //Bind This so I can use this.setState
this.changeProjectName = this.changeProjectName.bind(this); //Bind This so I can use this.setState
}
componentDidMount() {
this.props.fetchProjectTypes(); //This fetched the table from the nodejs server
}
changeEditMode() {
this.setState({ editMode: !this.state.editMode }); //Convert into edit mode and change rows in the table to inputs
}
changeProjectName(event){
//this.setState - Unsure how to set state of a particular array HERE IS THE PROBLEM
}
render() {
if (!this.props.projectTypes) {
return (
<CenterLoader /> //loading the table from server - show loader
)
}
else
return (
<div className="container">
<table className="ProjectType-Table">
<tbody>
<tr>
<th>
Id
</th>
<th>
Project Type
</th>
<th>
</th>
</tr>
{this.props.projectTypes.map((projectType, i) => { //RENDER ALL ROWS
return (
<tr key={i}>
<td>
{projectType._id}
</td>
<td>
{this.state.editMode ?
<input type="text" className="browser-default" defaultValue={projectType.name} onChange={this.changeProjectName}/> //On change, I need to save back to the state this value
:
projectType.name
}
</td>
<td>
<button className="btn btn-small mr-1" onClick={this.changeEditMode}>Edit</button>
<button className="btn btn-small">Delete</button>
</td>
</tr>
)
}
)}
</tbody>
</table>
</div>
);
}
}
function mapStateToProps(state) {
return { projectTypes: state.quizz.projectTypes };
}
export default connect(mapStateToProps, actions)(AddProjectType);
Attach data-idx={i} in the input like this,
<input type="text" className="browser-default" defaultValue={projectType.name} onChange={this.changeProjectName} data-idx={i}/>
In your changeProjectName(event) handler,
var index = event.target.dataset.idx; // Here you will get index

ReactJS - Adjacent JSX elements must be wrapped in an enclosing tag Error. When trying to include two <tr> tag

Am working in the Reactjs application with mongoDB. Am getting data from MongoDB and displaying in the table. Due to the table row is too long. I want to split the table row into two lines.
So, for this am trying to include two <tr> tags but it giving me the error Adjacent JSX elements must be wrapped in an enclosing tag.
I have written 3 separate component class for this requirement.
Please find my code below. ( Am using ReactJS 15.6.1 version )
class Details extends React.Component {
constructor(props) {
super(props);
this.state = { item: props.item };
}
render() {
return (
<table className="table table-bordered" style={{fontsize: '8'}}>
<thead>
<tr>
<th>Line Number</th>
<th>Product Code</th>
<th>Description</th>
<th>Quantity</th>
<th>Unit Price</th>
</tr>
</thead>
<DetailsList items={ this.state.item.order_items } />
</table>
);
}
}
class DetailsList extends React.Component {
constructor(props) {
super(props);
this.state = { };
}
render() {
return (<tbody>{ this.props.items.map((item) => <DetailsItem key={
item.line_id } item={ item } />) }
</tbody>);
}
}
class DetailsItem extends React.Component {
constructor(props) {
super(props);
this.state = { item: props.item };
}
render() {
return (
<tr>
<td><input name="line_number" type="text"
value={ this.state.item.line_number } /> </td>
<td><input name="product_code" type="text"
value={ this.state.item.product_code } /></td>
<td><input name="product_description" type="text"
value={ this.state.item.product_description } /> </td>
</tr>
<tr>
Product Quantity <td>{ this.state.item.product_quantity } />
</td>
Unit Price <td>{ this.state.item.unit_net_price } /></td>
</tr>
);
}
}
For Example am trying to like below snippet.
return(
<tr>
<td>.....</td>
<td>.....</td>
</tr>
<tr>
<td>.....</td>
<td>.....</td>
</tr>
)
I already wrapped my <tbody> tag with props data in my previous component.
render() {
return (<tbody>{ this.props.items.map((item) =>
<DetailsItem key={ item.line_id } item={ item } />) } </tbody>);
}
}
If i use single <tr> tag instead of two, then its working fine. But i want to split this row into two.
Please find the screenshot after adding <div> tag
Yes, JSX does not allow unwrapped adjacent HTML elements. This is because React uses Javascript to create the DOM elements. So something like this:
return (
<h2>Hello</h2>
<h1>World</h>
)
causes React to call document.createElement() twice, one for the <h2> and one for <h1/>, and this is not allowed.
If you really need to separate your table rows rather render them as an array. That is:
return([
<tr>
<td>.....</td>
<td>.....</td>
</tr>
<tr>
<td>.....</td>
<td>.....</td>
</tr>
])
Please note though, that, this approach works for at least React16. Otherwise, I would suggest to simply wrap these separated table rows with a div. That is:
return(
<div>
<tr>
<td>.....</td>
<td>.....</td>
</tr>
<tr>
<td>.....</td>
<td>.....</td>
</tr>
</div>
)
I would also like to point out that, this does not have to be a div. It could be other HTML elements, the point is to return one HTML element inside the render() of your component.
If you are using React 16+, you can return an array.
return [
<tr>
<td>.....</td>
<td>.....</td>
</tr>,
<tr>
<td>.....</td>
<td>.....</td>
</tr>
]

Render table when getting data asynchronous

I have some data which i get in state when componentDidMount.
I am trying render table using this data.
In my case rows not rendering.
How i can send data to tbody ?
export default class App extends Component {
constructor(props) {
super(props);
this.state={rows:null}
}
componentDidMount(){
var rows=[]
Meteor.http.call("GET", url ,function(error,result){
$.each(JSON.parse(result.content), function(key, value){
rows.push(value)
});
this.setState({
rows:rows});
})
}
renderRows(){
$.each(this.state.rows, function(d){
return(
<tr>
<td>{d[0]}</td>
<td>{d[1]}</td>
<td>{d[2]}</td>
<td>{d[3]}</td>
</tr>
)
})
}
render(){
return(
<Table>
<thead>
<tr>
<th>col1</th>
<th>col2</th>
<th>col3</th>
<th>col4</th>
</tr>
</thead>
<tbody>
{this.renderRows}
</tbody>
</Table>
)
}
}
Another option, not using JQuery and avoiding having a separate render function, is to use .map
React likes it if you have a unique key on each element in a list, so hopefully one of the fields on your row can serve this purpose.
render(){
return(
<Table>
<thead>
<tr>
<th>col1</th>
<th>col2</th>
<th>col3</th>
<th>col4</th>
</tr>
</thead>
<tbody>
{this.state.rows.map(d => (
<tr key={d[0]}>
<td>{d[0]}</td>
<td>{d[1]}</td>
<td>{d[2]}</td>
<td>{d[3]}</td>
</tr>
)}
</tbody>
</Table>
)
}
You'll also need to set your initial state for rows to be [] rather than null, in order for the first render to work.
renderRows is a function so you need to execute it. Also you will need to update that function a bit:
export default class App extends Component {
// ...
componentDidMount(){
var rows=[];
var self = this;
Meteor.http.call("GET", url ,function(error,result){
$.each(JSON.parse(result.content), function(key, value){
rows.push(value)
});
self.setState({
rows: rows
});
});
}
renderRows(){
const rows = this.state.rows || [];
return rows.map(d => {
return(
<tr>
<td>{d[0]}</td>
<td>{d[1]}</td>
<td>{d[2]}</td>
<td>{d[3]}</td>
</tr>
);
});
}
render(){
return(
<Table>
{/* ... */}
<tbody>
{this.renderRows()}
</tbody>
</Table>
)
}
}

Resources