i'm creating a web app in Reactjs with a ranking table all managed by firebase, but i came across a doubt:
after sorting the table based on who wagered the most money with the 'order by' command, I wish I could be able to change the position number, for example if it was second it becomes first (from 2 to 1).
That's my screen:
That's my code:
import React, {useState, useEffect} from 'react'
import './Table.css'
import {firebase} from '../../firebase'
function Table() {
const [peopleShow, setPeopleShow] = useState([]);
const ref = firebase.firestore().collection("Lista");
console.log(ref);
function getData(){
ref
.orderBy("money", "desc")
.onSnapshot((querySnapshot) => {
const items = [];
querySnapshot.forEach((doc) => {
items.push(doc.data());
});
setPeopleShow(items);
})
}
useEffect(() => {
getData();
}, [])
return (
<section>
<div class="tbl-content">
<table cellpadding="0" cellspacing="0" border="0">
<tbody>
{peopleShow.map((val) => {
return(
<tr key={val.ID}>
<td>{val.ID}</td>
<td>{val.nationality}</td>
<td>{val.username}</td>
<td>{val.money} $ <i class="far fa-caret-up"></i></td>
<td>{val.lastbid}</td>
<td>{val.newbid} $</td>
</tr>
)
})}
</tbody>
</table>
</div>
</section>
)}
export default Table
so i was wondering to solve this doubt, should i do a bubble sort? I state that the positions of the table I had defined momentarily with the id of my database. Thanks to all!!
You can use the index in the map function instead of making the number dependant on the data.
{peopleShow.map((val, index) => {
return(
<tr key={val.ID}>
<td>{index + 1}</td>
<td>{val.nationality}</td>
<td>{val.username}</td>
<td>{val.money} $ <i class="far fa-caret-up"></i></td>
<td>{val.lastbid}</td>
<td>{val.newbid} $</td>
</tr>
)
})}
Related
I rendered a table of inventory a small business carries (stored in JSON file).
I get this error in my console:
"Warning: Each child in a list should have a unique "key" prop.
Check the render method of Table
My App returns Table
<Table wines={wines}/>
My Table component:
import React from 'react'
import Row from './Row'
const Table = ({ wines,wine }) => {
return (
<div >
<table >
<tbody >
{wines.map(wine =>(
<Row wine={wine}/>
))}
</tbody>
</table>
</div>
)
}
export default Table
Row component:
import React from 'react'
import Cell from './Cell'
const Row = ({ wine }) => {
return (
<tr>
{Object.entries(wine).map(([key, value]) => {
return (
<Cell key={key} cellData={JSON.stringify(value)}/>
)
} ) }
</tr>
)
}
export default Row
Cell component:
import React from 'react'
const Cell = ({cellData,wine}) => {
return (
<td >
{cellData}
</td>
)
}
export default Cell
The table renders fine with the data, but I cannot understand why that error above still appears in the console. I am new to React and in the learning process. Thank you.
In your Table component, there is a key prop missing, eg:
{wines.map(wine =>(
<Row key={wine} wine={wine}/>
))}
It's important that the key prop is something unique to the item being iterated, as this is used to ensure the correct items are being updated, in the case where the component has to be re-rendered.
I am trying to read Database Data into a Table but I am getting an empty table from my Output though I am convinced I am doing the right thing.
Below is My code in the useEffect Hook and how I am Calling the data in the Table data jsx element.
Intervening for your Help
My Use useEffect Code
useEffect(() => {
const readCustomersData= async() =>{
const data = await getDocs(customersCollectionRef);
setCustomers(data.docs.map((doc) => ({ ...doc.data(), id:doc.id})));
}
readCustomersData();
},[])
How I am Calling The code in The jsx Elememnt
{ customers.map(( value, index) => {
return(
<tr key={index.id}>
<th scope="row">{index+1}</th>
<td>{value.cname}</td>
<td>{value.contact}</td>
<td>{}</td>
<th>{}</th>
<td>{}</td>
<td>{}</td>
<td>{}</td>
<td>{}</td>
<td className='p-2 d-flex justify-content-space-between'>
<Button variant='success'>Update</Button> <Button variant='danger'>Delete</Button>
</td>
</tr>
)
})}
</tbody>
I need to highlight rows in red color if the value of the ratio (column) is equal to or greater than 0.96. I was not sure where to add the lines to make these changes. Can anyone help me with this? I am trying to highlight the rows with red color where the condition satisfies. i.e. if the ratio value is greater than or equal to 0.96.
Here is the code I have:
import React, { useState, useContext } from "react";
import { AppContext } from "../../context/AppContext";
import { getCostSales} from "../../API/api";
import Button from "react-bootstrap/Button";
import Table from "react-bootstrap/Table";
const CostSales = () => {
const {
userRole,
employee,
setUserRole,
setEmployee,
isLoggedIn,
setIsLoggedIn
} = useContext(AppContext);
const [tableData, setTableData] = useState([]);
// TODO: conditional render for table? or can show table headers at least?
const onHandleRunCostSales = () => {
//call API/api method here
console.log("Run below cost sales report clicked:");
getCostSales().then((res) => {
if (res) {
setTableData(res);
console.log("res in below cost sales report: ", res);
}
});
};
return (
<div>
{userRole === "Manager" || userRole === "Owner" ? (
<div>
<Button variant="primary" onClick={onHandleRunCostSales}>
Run Report
</Button>
{tableData ? (
<div>
<Table>
<thead>
<tr>
<th>Vin</th>
<th>Date</th>
<th>Invoice Price</th>
<th>Sold Price</th>
<th>Ratio</th>
<th>Customer Name</th>
<th>SalesPerson</th>
</tr>
</thead>
<tbody>
{tableData.map((rowData, index) => (
<tr>
<td>{rowData.vin}</td>
<td>{rowData.date}</td>
<td> {rowData.invoice_price} </td>
<td>{rowData.sold_price}</td>
<td>{rowData.ratio}</td>
<td>{rowData.customer_name}</td>
<td>{rowData.salesperson}</td>
</tr>
))}
</tbody>
</Table>
</div>
) : (
<div>
<p>No data available for report</p>
</div>
)}
</div>
) : (
<div>
<p>Sorry, we can't show you this page</p>
</div>
)}
</div>
);
};
export default CostSales;
You can add the conditional style for tr element based on ratio
Update: you can have util method to decide on color based on ration
function highlightColor(ratio) {
if (ratio > 0.98) {
return "green";
} else if (ratio >= 0.96) {
return "red";
}
return "white";
}
<tbody>
{tableData.map((rowData, index) => (
<tr style={{ backgroundColor: highlightColor(rowData.ratio) }}>
<td>{rowData.vin}</td>
<td>{rowData.date}</td>
<td> {rowData.invoice_price} </td>
<td>{rowData.sold_price}</td>
<td>{rowData.ratio}</td>
<td>{rowData.customer_name}</td>
<td>{rowData.salesperson}</td>
</tr>
))}
</tbody>;
Im receving some products on props in the OrderContent component to use them in a select component, when I select the product in the select it renders Summary and Product components, in those components I can choose the quantity and with that I can calculate the total all back on the OrderContent Component, the problem is when im trying to use the OnChange in the input type (on Product component), useEffect (inside is the function that calculates the total in the state) doesnt trigger but it does if I add a product from the state or remove it.
import React, { Fragment, useState, useEffect } from "react";
import Select from "react-select";
import Animated from "react-select/lib/animated";
import Summary from './Summary';
function OrderContent({ products }) {
const [productsSelected,setProductsSelected] = useState([]);
const [total,setTotal] = useState(0);
useEffect(() => {
updateTotal()
}, [productsSelected]);
const selectProduct = (prod)=>{
setProductsSelected(prod)
}
const updateQuantity = (val,index)=>{
const tempProds = productsSelected;
tempProds[index].quantity= Number(val);
setProductsSelected(tempProds)
}
const deleteProduct = (id) =>{
const tempProds = productsSelected;
const remProds = tempProds.filter((p)=> p.id !== id );
setProductsSelected(remProds);
}
const updateTotal = () =>{
const tempProds = productsSelected;
if(tempProds.length === 0){
setTotal(0)
return;
}
let newTotal = 0;
tempProds.map((p)=>{
const q = p.quantity ? p.quantity : 0;
newTotal = newTotal + (q * p.price)
})
setTotal(newTotal)
}
return (
<Fragment>
<h2 className="text-center mb-5">Select Products</h2>
<Select
onChange={selectProduct}
options={products}
isMulti={true}
components={Animated()}
placeholder={"Select products"}
getOptionValue={options => options.id}
getOptionLabel={options => options.name}
value={productsSelected}
/>
<Summary
products={productsSelected}
updateQuantity={updateQuantity}
deleteProduct = {deleteProduct}
/>
<p className="font-weight-bold float-right mt-3">
Total:
<span className="font-weight-normal">
${total}
</span>
</p>
</Fragment>
);
}
export default OrderContent;
import React, {Fragment} from 'react';
import Product from './Product';
function Summary({products,updateQuantity,deleteProduct}) {
if(products.length === 0) return null;
return (
<Fragment>
<h2 className="text-center my-5">Summary and Quantities</h2>
<table className="table">
<thead className="bg-success text-light">
<tr className="font-weight-bold">
<th>Product</th>
<th>Price</th>
<th>Inventory</th>
<th>Quantity</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{products.map((p,index)=>{
return (<Product
key={p.id}
id={p.id}
product={p}
index={index}
updateQuantity={updateQuantity}
deleteProduct={deleteProduct}
/>)
})}
</tbody>
</table>
</Fragment>
)
}
export default Summary
import React, { Fragment } from "react";
function Product({ product, updateQuantity, index, deleteProduct }) {
return (
<Fragment>
<tr>
<td>{product.name}</td>
<td>${product.price}</td>
<td>{product.stock}</td>
<td>
<input
type="number"
className="form-control"
onChange={e => updateQuantity(e.target.value, index)}
/>
</td>
<td>
<button type="button" className="btn btn-danger font-weight-bold" onClick={e=> deleteProduct(product.id)}>
× Delete
</button>
</td>
</tr>
</Fragment>
);
}
export default Product;
updateQuantity is mutating state. This means that react will see that you've tried to update state with the same object reference and the re-render will be skipped, meaning no useEffect triggers.
Change it to this to create a new array with new nested objects:
const updateQuantity = (val,index)=>{
const tempProds = [...productsSelected.map(val => {...val})];
tempProds[index].quantity= Number(val);
setProductsSelected(tempProds)
}
deleteProduct doesn't mutate because filter returns a new array. But setting the tempProds is completely unnecessary.
updateTotal also mutates state, but only its nested objects. So this still needs to be fixed, but will probably not cause the same re-render issue.
Based on the use of const tempProds = productsSelected in several places, I think you should do some research on how JavaScript objects are assigned and referenced. There's plenty of resources out there, but I wrote a pretty detailed explanation as part of this answer.
If productsSelected is the same array then useEffect can't detect the change because it's always pointing to the same object
const selectProduct = (prod)=>{
setProductsSelected([...prod])
}
To force the product selected to be a new array
I get data from github api
I have all the data i need to display, but I want to splice it so that i only get 20 repositories per page.
And I don't want a framework or a plugin for that.
I'm fairly new to React and JS in general so I don't know where to start or what to do next to create a pagination.
import React, {Component} from 'react';
import axios from 'axios';
class Apirequest extends Component {
constructor(){
super();
this.state = {
githubData: [],
};
}
componentDidMount() {
axios.get('https://api.github.com/search/repositories?q=language:javascript&sort=stars&order=desc&per_page=100')
.then(res => {
console.log('res', res)
this.setState({ githubData: res.data.items})
})
}
render() {
const { githubData } = this.state
return(
<div className="container">
{githubData.map((name, index) =>
<table key={name.id}>
<tr>
<th><img src={name.owner.avatar_url}/></th>
<td>{name.owner.login}<div className="border-bottom"></div></td>
<td>{name.description}<div className="border-bottom"></div></td>
<td><a href={name.homepage}>{name.homepage}</a></td>
</tr>
</table>
)}
</div>
)
}
}
export default Apirequest;
First of all your map function has a wrong logic. You are creating a table for each record and you should only create a row for each record. table tags should be outside of map.
render() {
const { githubData } = this.state
return(
<div className="container">
<table key={name.id}>
{githubData.map((name, index) =>
<tr>
<th><img src={name.owner.avatar_url}/></th>
<td>{name.owner.login}<div className="border-bottom"></div></td>
<td>{name.description}<div className="border-bottom"></div></td>
<td><a href={name.homepage}>{name.homepage}</a></td>
</tr>
)}
</table>
</div>
)
}
For pagination what you can do is to limit the number of rows you show by using Array.prototype.slice(). Just to give you an idea I am posting a small example. You might need to implement some more for this logic to work on your code.
Example
previousPage = () => {
if (this.state.currentPage !== 1)
this.setState((prevState) => ({currentPage: (prevState.currentPage - 1)}))
}
nextPage = () => {
if (this.state.currentPage + 1 < this.state.githubData.lenght)
this.setState((prevState) => ({currentPage: (prevState.currentPage + 1)}))
}
render() {
const { githubData, currentPage } = this.state
return(
<div className="container">
<table key={name.id}>
{githubData.slice((currentPage * 20), 20).map((name, index) =>
<tr>
<th><img src={name.owner.avatar_url}/></th>
<td>{name.owner.login}<div className="border-bottom"></div></td>
<td>{name.description}<div className="border-bottom"></div></td>
<td><a href={name.homepage}>{name.homepage}</a></td>
</tr>
)}
</table>
<button onClick={this.previousPage}>Previous Page</button>
<button onClick={this.nextPage}>Next Page</button>
</div>
)
}
set your state to have pagination info and data.
such as
state = {
pagination: {
start: 0,
rows: 20
},
githubData: ....
}
and now in you render function you can splice based on the pagination info. Anytime new page is clicked, you can set state to new start variable