I am trying to create a table looking for data from firebase, however the table is always giving error.
I put the whole function in a script to validate if any error in the component call, but always lists the same error: TypeError: custos.map is not a function
Does anyone know what can it be?
I already tested it and the variable custos can return the object, but I can't use it in the map function
function MeusCustos(){
const [custos, setCustos] = useState([]);
const [pesquisa, setPesquisa] = useState('');
const usuarioEmail = useSelector(state => state.usuarioEmail);
let listaCustos = [];
useEffect( () => {
firebase.firestore().collection('custos').get().then(async(resultado) => {
resultado.docs.forEach(doc => {
listaCustos.push({
id: doc.id,
...doc.data()
});
});
setCustos(JSON.stringify(listaCustos));
});
}, []);
return(
<>
<Navbar/>
<div className="row p-3">
<div className="container">
<h1>Simple Inventory Table</h1>
<table className="table table-striped">
<thead>
<tr>
<th scope="col">Titulo</th>
<th scope="col">Descrição</th>
<th scope="col">Quantidade</th>
<th scope="col">Valor</th>
<th scope="col">Data</th>
<th scope="col">Comprovante</th>
<th scope="col">UsuarioEmail</th>
<th scope="col">Visualizações</th>
<th scope="col">Publico</th>
<th scope="col">Criação</th>
</tr>
</thead>
<tbody>
{custos.map( item =>
<tr >
<th scope ="row"></th>
<td>{item.titulo}</td>
<td>{item.descricao}</td>
<td>{item.quantidade}</td>
<td>{item.valor}</td>
<td>{item.data}</td>
<td>{item.comprovante}</td>
<td>{item.usuarioemail}</td>
<td>{item.visualizacoes}</td>
<td>{item.publico}</td>
<td>{item.criacao}</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
</>)
}
export default MeusCustos;
The problem is in your useEffect hook. Do not stringfy the array because it will become a string and the map function works on arrays, change setCustos(JSON.stringify(listaCustos)); so that your useEffect will look as follows:
useEffect( () => {
firebase.firestore().collection('custos').get().then(async(resultado) => {
resultado.docs.forEach(doc => {
listaCustos.push({
id: doc.id,
...doc.data()
});
});
setCustos(listaCustos);
});
}, []);
I found the problem, one of the values had been written with sysdate, which brought data of seconds and nanoseconds breaking the date component
Tks 4 help guys
Related
Let me explain the issue, I am trying to show some details in a modal. I am using table in the modal to show details. In one section, I need the product name which is returned from a database query and I have to use that value returned from the database. The code for table is as follows:
<tbody>
{item.cartItems.map((item, i) =>
<tr key={item._id}>
<th scope="row">{i + 1}</th>
<th><img src={`${API}/product/photo/${item.product}`} alt={item.product.name} width="60px" height="50px" /></th>
<td>{**data returned from database**}</td>
<td align="center">{item.count}</td>
<td align="center">৳ {item.price * item.count} </td>
</tr>
)}
</tbody>
To get the data from the database, I am using a function
const getProdcutName = id => {
var productName;
getProductDetails(id)
.then(response => {
productName = response.data.name;
});
return productName;
}
But I can't access the value. The main thing is, in every Iteration I need to send the {item.product} to getProductName(id) and I need the name from the database in return. But I can't access the data from promise scope to return it.
This is the solution to my issue. Thanks to everyone who helped me out.
const CartItem = ({ item, i }) => {
const [productName, setProductName] = useState();
useEffect(() => {
getProductDetails(item.product)
.then(res => { setProductName(res.data.name) });
}, []);
return (
<tr key={item._id}>
<th scope="row">{i + 1}</th>
<th><img src={`${API}/product/photo/${item.product}`} alt={item.product.name} width="60px" height="50px" /></th>
<td>{productName == undefined ? "Getting Name" : productName}</td>
<td align="center">{item.count}</td>
<td align="center">৳ {item.price * item.count} </td>
</tr>
)}
And to send items to the cartItem
<tbody>
{item.cartItems.map((item, i) =>
<CartItem item={item} i={i} key={item._id} />
)}
</tbody>
i would like to output 10 times before changing column, I saw some other examples but I didn't understand, im quite new to react. Furthermore i would like to show different timeslots in the first column 07:00-08:30, 08:30-10 and so on, with the setup i have at the moment how would i go about doing that?
function BookingTable() {
useEffect(() => {
Axios.post('http://localhost:3001/api/get/week1').then((response) => {
setIsBooked(response.data)
console.log(response.data);
})
}, []);
const [isBooked, setIsBooked] = useState([])
const renderTableData = () => {
return isBooked?.map((val) => (
<tr class>
<td></td>
<td>{val.booked}</td>
</tr>))
}
return (
<table id="table">
<thead>
<tr>
<th> </th>
<th>Monday</th>
<th>Tuesday</th>
<th>Wednesday</th>
<th>Thursday</th>
<th>Friday</th>
<th>Satuday</th>
<th>Sunday</th>
</tr>
</thead>
<tbody>
{renderTableData()}
</tbody>
</table>
)
}
export default BookingTable
How can I use .map so I can get the data for rentals and display the movie title as well? Do I use two .map functions or do I have to use a useEffect?
Movies
Id
Name
Price
Rentals
Id
Movie Id
Date Rented
Customer email
const RentalPage = () => {
const [movies, setMovies] = useState([]);
const [rentals, setRentals] = useState([]);
useEffect(() => {
fetchRentals(setRentals);
}, []);
useEffect(() => {
fetchMovies(setMovies);
}, []);
return (
<div>
<div>
<div>
<table>
<thead>
<tr>
<th>Customer Email</th>
<th>Movie Title</th>
<th>Purchase Date</th>
<th>Price</th>
</tr>
</thead>
{rentals.map((rental) => (
<tbody key={rental.id}>
<tr>
<td>{rental.customerEmail}</td>
<td >{movie.movieTitle}</td> <----------- (How do I use .map to get movie name)
<td>{rental.purchaseDate}</td>
<td>{rental.price}</td>
</tr>
</tbody>
))}
</table>
</div>
</div>
</div>
);
}
My question is since I am already using .map, can I use it again with different data without changing the database?
You can use find or filter
{rentals.map((rental) => {
// find
const movie_title = movies.find(movie => movie.id === rental.movieId).movieTitle
// or by filter
let _movie_title = movies.filter(movie => movie.id === rental.movieId)[0].movieTitle
return <tbody key={rental.id}>
<tr>
<td>{rental.customerEmail}</td>
<td >{movieTitle}</td>
<td>{rental.purchaseDate}</td>
<td>{rental.price}</td>
</tr>
</tbody>
})}
find returns the element that matches in the criteria, and filter returns a sublist of items that matched the criteria.
When I log the values from snap parameter it is displaying the values there but the rows created by renderTable function aren't rendered in a browser.
function Employees() {
const rootRef = firebase
.database()
.ref()
.child("Employees");
const renderTable = () => {
var i = 0;
return rootRef.on("value", snap => {
snap.forEach(keys => {
i++;
return (
<tr key={keys}>
...
</tr>
);
});
});
};
return (
<div className={styles.Employee}>
<h1 className={styles.header}>Employees</h1>
<table>
<thead>
<tr>
...
</tr>
</thead>
<tbody>{renderTable()}</tbody>
</table>
</div>
);
}
export default Employees;
Seems like firebase's on() method invokes callback asynchronously. You must change your approach and use state and lifecycle in your React component, ie. with some help of hooks useEffect and useState as well as map method instead of forEach. In example:
function Employees() {
const [employees, setEmployees] = useState([]);
useEffect(() => {
firebase
.database()
.ref()
.child("Employees")
.on("value", setEmployees);
}, []);
const renderTable = () => {
return employees.map((employee, i) => {
return (
<tr key={keys}>
...
</tr>
);
});
};
return (
<div className={styles.Employee}>
<h1 className={styles.header}>Employees</h1>
<table>
<thead>
<tr>
...
</tr>
</thead>
<tbody>{renderTable()}</tbody>
</table>
</div>
);
}
export default Employees;
This the where rootRef is refering to
So I updated this and it kinda works.When I log The Emp attribute in JSX it contains an array of Table rows which is how it is suppose to be. When I do this it is Rendering the last node of array. and when i use .map function on it to render all of the array it shows this error TypeError: Emps.map is not a function.
How else am i suppose to render this array?
function Employees() {
const rootRef = firebase.database().ref().child("Employees")
var i = 0
const [Emps, setEmployees] = useState([])
useEffect(() => {
rootRef.on('value', snap => {
snap.forEach(keys => {
i++
setEmployees(
<tr>
<td>{i}</td>
<td>{keys.child("Name").val()}</td>
<td>{keys.child("employee_type").val()}</td>
<td>{keys.child("email").val()}</td>
<td>{keys.child("phoneNum").val()}</td>
<td>
<input className={styles.view} name="view" type="submit" value="View"/>
</td>
</tr>
)
})
})
}, [])
return (
<div className={styles.Employee}>
<h1 className={styles.header}>Employees</h1>
<table>
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Employee Type</th>
<th scope="col">Email</th>
<th scope="col">Phone #</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody>
{
Emps
}
</tbody>
</table>
</div>
)
}
export default Employees
I am trying to get a click even to work with a table in reactjs. My first attempt was to make the whole row clickable. Here is my code:
var UserList = React.createClass({
getInitialState: function() {
return getUsers();
},
handleClick: function(e) {
console.log("clicked");
},
render: function() {
var users = this.state.users.map(function(user) {
return (
<tr onClick={this.handleClick}>
<td>{user.name}</td>
<td>{user.age}</td>
<td></td>
</tr>
);
});
return(
<div className="container">
<table className="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Full Detail</th>
</tr>
</thead>
<tbody>
{users}
</tbody>
</table>
</div>
);
}
});
This did not work. I then tried to add a button in the table:
<button className="btn" onClick={this.handleClick}>Full Detail</button>
That also did not work. I have other onClick's working throughout my app, but how do I make this work with a table?
Your problem is the function of user that creates the table row is not bound to your react component. The value of this will not be your react component and handleClick will not exist as a property of this.
Try
var users = this.state.users.map(function(user) {
return (
<tr onClick={this.handleClick}>
<td>{user.name}</td>
<td>{user.age}</td>
<td></td>
</tr>
);}.bind(this);
});
Or use Underscore's bind if you want it to work on all browsers.
I'm new to react. How about this? You just wrap it in another function, then that function holds the closure scope and it calls it correctly.
No idea if this is bad practice or the performance difference, but it seems to work...
var users = this.state.users.map(function(user) {
return (
<tr onClick={()=>this.handleClick(user)}>
<td>{user.name}</td>
<td>{user.age}</td>
<td></td>
</tr>
);}.bind(this);
});
Binding creates a new object. Thus if you bind your function for N employees, you are inefficiently creating N new functions. A more elegant approach is to bind the function once, and pass a reference to every row. Your original code was quite close. This is what I would suggest:
handleClick = e => {
const user = this.state.users.find(u => u.uuid == e.target.dataset.uui)
console.log("clicked");
},
render() {
return(
<div className="container">
<table className="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Full Detail</th>
</tr>
</thead>
<tbody>
{this.state.users.map(user =>
(
<tr data-uuid={user.uuid} onClick={this.handleClick}>
<td>{user.name}</td>
<td>{user.age}</td>
<td>{user.details || ''}</td>
</tr>
)
)}
</tbody>
</table>
</div>
);
}
});