I'm trying to create a dynamic table so that every time I press the "add" button, a new row will be created.
In my example below, it creates only 1 row with all the data under its header, but with all the 3 values in this case instead of create a new row.
I'm fetching the document from Firestore and the collection includes only 1 document with an array inside, in this case with 3 values.
here is the screenshot of the table it creates:
Expecteed table example:
Code:
export default class NutritionTableOfTheUser extends Component {
constructor() {
super();
this.state = {
tableData: []
}
}
componentDidMount() {
const dbRef = collection(db, 'data');
onSnapshot(dbRef, (querySnapshot) => {
let foods = [];
querySnapshot.forEach(doc => {
foods.push(doc.data())
});
this.setState({ tableData: foods })
})
}
render() {
return (
<div className='container mt-3'>
{/* table */}
<table className='table table-hover'>
<thead>
<tr>
<th>#</th>
<th>Food</th>
<th>Quantity</th>
</tr>
</thead>
<tbody>
{this.state.tableData.map((row, index) => {
return (
<tr>
<td>{index + 1}</td>
<td>{row.value}</td>
<td>{row.quantity}</td>
</tr>
)
})}
</tbody>
</table>
</div>
)
}
}
You should check the data you are getting from firebase collection, because you might have only one document in collection which results in rendering only one row in table.
It looks like you only have a single document, and your .value and .quantity properties of the row are arrays (rather than numbers) - so doing <td>{row.value}</td> mashes all values of the array together.
Turn the .data() into the desired data format first, then set the state with it.
querySnapshot.forEach(doc => {
const [obj] = doc.data();
const tableData = obj.value.map((value, i) => ({
value,
quantity: obj.quantity[i]
}));
this.setState({ tableData })
});
Related
Data is an array object. I want to render table columns dynamically according to data object. I used this same technique to generate table head its working. Is there any way to fix this issue?
const DataTable = ({data, deleteHandler, loading, tableFields}) => {
const thead = [];
const tbody = [];
const [values, setValues] = useState({});
for (const [key, value] of Object.entries(tableFields)) {
thead.push(<td>{value}</td>);
}
data.map((field) => {
for(const[key,value] of Object.entries(field)){
tbody.push(
<tr>
<td>{value}</td>
</tr>
);
}
})
return (
<Fragment>
<Card className={classes.__bk_admin_card}>
<Table borderless hover className={classes.__bk_admin_table}>
<thead>
<tr>
{thead}
</tr>
</thead>
<tbody>
{tbody}
</tbody>
</Table>
</Card>
</Fragment>
)
}
I'm using React to sort table using underscore.js, but the sorted data is added to the bottom of the table along with the original data. I need to just render the sorted table in my component. What is the correct way to achieve this?
class Players extends React.Component {
constructor() {
super();
this.state = { players:[]};
}
componentDidMount() {
fetch('http://localhost:5000/players')
.then((data) => data.json())
.then((data) => this.setState( { players: data } ));
}
sortByCode = () => {
this.setState( { "players": _.sortBy(this.state.players,'TEAMCODE')});
}
render() {
return (
<table border={1}>
<tbody>
<tr>
<th onClick={this.sortByCode}>Code</th>
<th>Name</th>
</tr>
{this.state.players.map( (item) => {
return
<tr key={item.TEAMCODE}>
<td><img height={20} width ={20}
alt="pics" src={'/logos/' + item.TEAMCODE + '_logo.svg'}></img>
</td>
<td>{item.TEAMCODE}</td>
<td>{item.NAME}</td></tr>;
})}
</tbody>
</table>
)};
It's not recommended to use the index as a key (see ReactJS documentation)
I would advise to find the unique key of your dataset that seems to be the combination of TEAMCODE and NAME so you should replace your code with :
<tr key={item.TEAMCODE+item.NAME}>
I am learning programming and have an issue. I would like to iterate and loop (i think are the terms) a row. The row will contain 3 columns who's data comes from a REST api. Once the row has 3 columns fulled, I would like a new row to be created with 3 more columns.
What is the easiest way to achieve this thanks.
My code is here https://jsfiddle.net/Ory4n/8k7dabyr/1/ and I found a possible solution code here https://jsfiddle.net/dya52m8y/2/ however I do not know how to implement that code into my own project as of yet.
class Test extends Component {
constructor(){
super();
this.state = {
pages: []
}
}
componentDidMount() {
let pagesURL = "http://creartem.nz/wp-json/wp/v2/projects";
fetch(pagesURL)
.then(response => response.json())
.then(response => {
this.setState({
pages: response
})
})
}
render() {
let pages = this.state.pages.map((page, index) => {
return (
{/* Start Loop */}
<div key={index}>
<h2>{page.title.rendered}</h2>
<p>{page.acf.technologies}</p>
<p>{page.acf.team}</p>
</div>
{/* end Loop */}
)
})
return (
<section>
{pages}
</section>
);
}
}
export default Test;
If you want to render an array of components, you must call a function that in turn returns a call to the .map() method you had specified. See below:
render() {
const renderPages = () => {
return this.state.pages.map((page, index) => {
return (
<div key={index}>
<h2>{page.title.rendered}</h2>
<p>{page.acf.technologies}</p>
<p>{page.acf.team}</p>
</div>
);
})
}
return (
<section>
{renderPages()}
</section>
);
}
I could be wrong, but if I understand correctly, you would like to create a new row containing a h2 and two p tags. You could try using a table to achieve this output. Something like:
const renderPages = () => {
return (
<div>
<table>
<tr>
<th></th>
<th></th>
<th></th>
</tr>
{this.state.pages.map(page => {
return (
<tr key={page.id}>
<td><h2>{page.title.rendered}</h2></td>
<td><p>{page.acf.technologies}</p></td>
<td><p>{page.acf.team}</p></td>
</tr>
)
})}
</table>
</div>
)
}
Also, avoid using index as your key. It causes weird issues. Use something like an id.
Here is a similar example on codepen: https://codepen.io/mkempinsky/pen/gBapqg?editors=1111
Hi I want to have multiple TRs and inside one, have multiple TDs using react I want to loop through my comparedProperties object and create the table in render method dynamically but I get this error:
Uncaught Error: Objects are not valid as a React child (found: object with keys {id, address, long, lat, cityId, cityDistrict, phone,
name, userId, city}). If you meant to render a collection of
children, use an array instead or wrap the object using
createFragment(object) from the React add-ons. Check the render method
of Comparison.
my data object is like this and I can not change its structure:
//this is a samle data, keys in this object can dynamically change elsewhere
let comparedProperties = {
id: [1001,1002],
address: ["abc","def"],
};
this is my code:
class Comparison extends Component {
render() {
let comparedProperties = {
id: [1001, 1002],
address: ["abc", "def"]
};
let comparedItemsData = [];
for (var key in comparedProperties) {
if (comparedProperties.hasOwnProperty(key)) {
let newTR = <tr key={Math.random()} className="compare-table-row">
<td className="table-item-header">
{key}
</td>
{comparedProperties[key].map((item) => {
return <td key={Math.random()} className="table-item">{item}</td>;
})}
</tr>;
comparedItemsData.push(newTR)
}
}
return (
<table className="compare-table">
<tbody>
{comparedItemsData}
</tbody>
</table>
)
}
}
const mapStateToProps = (state) => ({
...state
});
const mapDispatchToProps = (dispatch) => ({
actions: bindActionCreators(Actions, dispatch)
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(Comparison);
update answer:
so I figuerd where the problem was but I expexted better error message from react
the problem was that in my comparedProperties I had an object inside the array that caused the error
let comparedProperties = {"id":[101,102],"estateAgency":[{"id":1},{"id":2}]}
Are you trying to do something like that ?
render(){
let comparedProperties = {
id: [1001, 1002],
address: ["abc", "def"],
};
return (
<table>
{Object.keys(comparedProperties).map(key=>(
<tr key={Math.random()} className="compare-table-row">
<td className="table-item-header">
{key}
</td>
{comparedProperties[key].map((item) => (
<td key={Math.random()} className="table-item">{item}</td>
))}
</tr>
))}
</table>
)
}
Or if you want to try as a a stateless comp you insert in your table :
const ComparedItemsData = ({ comparedProperties }) =>(
<React.Fragment>
{Object.keys(comparedProperties).map(key => (
<tr key={Math.random()} className="compare-table-row">
<td className="table-item-header">{key}</td>
{comparedProperties[key].map(item => (
<td key={Math.random()} className="table-item">
{item}
</td>
))}
</tr>
))}
</React.Fragment>
)
const App = ()=>{
let comparedProperties = {
id: [1001, 1002],
address: ["abc", "def"]
};
return (
<table className="compare-table">
<tbody>
<ComparedItemsData comparedProperties={comparedProperties}/>
</tbody>
</table>
)
}
You just need to return the td elements from within map function. Also never specify Math.random() as a key to the react elements because everytime render is called a new key will be assigned and it will force React to re-render the entire component even though nothing would have changed.
for (var key in comparedProperties) {
if (comparedProperties.hasOwnProperty(key)) {
let newTR = <tr key={key} className="compare-table-row">
<td className="table-item-header">
{key}
</td>
//comparedProperties[key] is an array of
// values that I want to make them as td elements
{ comparedProperties[key].map((item) => {
return <td key={item} className="table-item">{item}</td>;
})}
</tr>;
comparedItemsData.push(newTR)
}
}
I am facing an issue to order the rows of a fetched API data dynamically, i.e if the rows data within the API is changed (+/- row) to render it automatically. I am trying to print the data fetched from an API into a table. Anyhow the columns are well printed dynamically, but the function that I am using for columns this.state.columns.map(( column, index ) => doesn't function in the same way for the rows. I think i am misleading the ES6 standard, but I am not sure. Here is how it's look like, if the rows are not hardcoded.
Here is my code sample:
class App extends React.Component
{
constructor()
{
super();
this.state = {
rows: [],
columns: []
}
}
componentDidMount()
{
fetch( "http://ickata.net/sag/api/staff/bonuses/" )
.then( function ( response )
{
return response.json();
} )
.then( data =>
{
this.setState( { rows: data.rows, columns: data.columns } );
} );
}
render()
{
return (
<div id="container" className="container">
<h1>Final Table with React JS</h1>
<table className="table">
<thead>
<tr> {
this.state.columns.map(( column, index ) =>
{
return ( <th>{column}</th> )
}
)
}
</tr>
</thead>
<tbody> {
this.state.rows.map(( row ) => (
<tr>
<td>{row[0]}</td>
<td>{row[1]}</td>
<td>{row[2]}</td>
<td>{row[3]}</td>
<td>{row[4]}</td>
<td>{row[5]}</td>
<td>{row[6]}</td>
<td>{row[7]}</td>
<td>{row[8]}</td>
<td>{row[9]}</td>
<td>{row[10]}</td>
<td>{row[11]}</td>
<td>{row[12]}</td>
<td>{row[13]}</td>
<td>{row[14]}</td>
<td>{row[15]}</td>
</tr>
) )
}
</tbody>
</table>
</div>
)
}
}
ReactDOM.render( <div id="container"><App /></div>, document.querySelector( 'body' ) );
Instead, I was able to print the rows harcoded, if I give value to the 'td' elements, but I want to print it dynamically, in case the data within the API has been changed.
You are welcome to contribute directly to my Repo: Fetching API data into a table
Here is how looks like my example, when the rows values has been hardcoded within 'td' elements.
Any suggestions will be appreciated!
Just as you are iterating over columns and rows you could add an additional loop for iterating over row cells.
For example:
this.state.rows.map(row => (
<tr>{row.map(cell => (
<td>{cell}</td>
))}
</tr>
))
Note that you probably want to add a key prop:
Keys help React identify which items have changed, are added, or are
removed. Keys should be given to the elements inside the array to give
the elements a stable identity