Django Rest Framework with React order not working - reactjs

I have a list in my application which is created from two related tables(products and stocks). I want to order items by date which is in the Stocks table. My API returns well ordered list but in my app the list is not ordered. When I try to order by any of Products fields it works fine - also in react.
My view:
class StocksView(viewsets.ModelViewSet):
serializer_class = StocksSerializer
ordering_fields = ['date']
filter_backends = (filters.OrderingFilter,)
def get_queryset(self):
queryset = Stocks.objects.all().order_by('date')
return queryset
def perform_create(self, serializer):
serializer.save(user=self.request.user)
Method from React:
const fetchStocks = async () => {
try {
const response = await axiosInstance.get('/stocks/?ordering=date')
if (response) {
setStocksInfo(response.data)
console.log(response.data)
}
} catch (error) {
throw error;
}
}
And returned list from two merged tables:
{productsInfo.map(products => {
const product = stocksInfo.filter(e => e.product_id === products.product_id);
return (
product.map((stock, i) => (
<tr key={i} className="table-data">
<td className="col-3"> {products.name}</td>
<td className="col-3">{stock.date}</td>
<td className="col-1">{stock.count}</td>
</tr>
))
)
})}

Since you want to order based on the Stocks table, you should do the map from that array first.
You can make an implementation like this one, where you filter the stocks so it only has the ones matching from the productsInfo, and then mapping it.
{stocksInfo
.filter(e => productsInfo.some(p => p.product_id === e.product_id))
.map((stock, i) => {
const products = productsInfo.find(e => e.product_id === stock.product_id);
return (
<tr key={i} className="table-data">
<td className="col-3"> {products.name}</td>
<td className="col-3">{stock.date}</td>
<td className="col-1">{stock.count}</td>
</tr>
);
})}
But, since that is inefficient, I would recommend combining both arrays and then mapping them. You can combine them with this code:
const combination = stocksInfo
.map(item =>
Object.assign(
item,
productsInfo.find(({product_id}) => product_id === item.product_id)
)
)
.filter(item => item.name);
I also added a working example here:
const stocksInfo = [
{date: "date1", count: 1, product_id: 123},
{date: "date2", count: 2, product_id: 321},
{date: "date3", count: 3, product_id: 331},
{date: "date4", count: 4, product_id: 341},
];
const productsInfo = [
{name: "test4", product_id: 341},
{name: "test2", product_id: 321},
];
const combination = stocksInfo
.map(item =>
Object.assign(
item,
productsInfo.find(({product_id}) => product_id === item.product_id)
)
)
.filter(item => item.name);
const Example = ({title}) => {
return (
<div>
previous way
<table>
{productsInfo.map(products => {
const product = stocksInfo.filter(e => e.product_id === products.product_id);
return product.map((stock, i) => (
<tr key={i} className="table-data">
<td className="col-3"> {products.name}</td>
<td className="col-3">{stock.date}</td>
<td className="col-1">{stock.count}</td>
</tr>
));
})}
</table>
new way
<table>
{combination.map((stock, i) => {
return (
<tr key={i} className="table-data">
<td className="col-3"> {stock.name}</td>
<td className="col-3">{stock.date}</td>
<td className="col-1">{stock.count}</td>
</tr>
);
})}
</table>
</div>
);
};
// Render it
ReactDOM.render(
<Example title="Example using Hooks:" />,
document.getElementById("react")
);
table, td {
border: 1px solid black;
padding: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Related

How can I filter and show an array with React?

I'm trying to filter some companies array -that is being shown in a table- using multiple checkboxes. These checkboxes returns an array of strings with some status. For example: ['accepted', 'pending', 'declined'];.
The problem is that while the checkboxes returns the status OK, I cannot manage to filter the companies array with the checkboxes data.
Here is my main code:
function App() {
const [filters, setFilters] = useState([]);
const [companies, setCompanies] = useState([
{ id: 1, status: "accepted", name: "001", date: "01/01/2022" },
{ id: 2, status: "accepted", name: "001", date: "01/01/2022" },
{ id: 8, status: "accepted", name: "001", date: "10/04/2022" },
]);
//HERE I TRY TO FILTER MY COMPANIES ARRAY WITH THE SELECTED CHECKBOXES
const showFilteredCompanies = (filters) => {
return [...companies].filter((company) => {
return filters.includes(company.status);
});
}
//Inside data I have my array of selected status like this: ['accepted', 'pending', 'declined']
const handleFilters = (data) => {
let newFilters = {...filters};
newFilters = data;
setFilters(newFilters);
// THIS CONSOLE.LOG SHOWS THE FILTERED ARRAY OF COMPANIES JUST FINE
console.log(showFilteredCompanies(newFilters));
const filtered = showFilteredCompanies(newFilters);
//BUT WHEN I TRY TO SAVE MY COMPANIES FILTERED ARRAY IN MY STATE THE FILTERING DOESN'T WORK AS IT SHOULD
console.log(filtered);
setCompanies(filtered);
}
return (
<div className="App">
<Checkbox
handleFilters={data => handleFilters(data)}
/>
{/* companies list */}
<div>
{companies.length ?
(
<table>
<thead>
<tr>
<th>ID</th>
<th>Estado</th>
<th>nombre</th>
<th>Fecha</th>
</tr>
</thead>
<tbody>
{companies.map((company, i) => (
<tr key={i}>
<td>{company.id}</td>
<td>{company.status}</td>
<td>{company.name}</td>
<td>{company.date}</td>
</tr>
)
)}
</tbody>
</table>
) : (
<h2>No hay compañías para mostrar</h2>
)
}
</div>
{/* list length */}
<div>
<h3>Cantidad de compañías: {companies.length}</h3>
</div>
</div>
);
}
export default App;
I was expecting to filter my companies array with all the strings of my checkboxes data array to show only the companies.status that matches the selected status of the checkboxes
It seems that the filtering of companies might not need to be another state, and could possibly be chained with map() in the output for simplicity.
Not sure if it would work without testing in the use case, but assuming that filters are updated just fine as posted, perhaps try omit showFilteredCompanies and setCompanies in handleFilters, and add in the output:
{ /* companies list with filter before map */ }
<div>
{companies.length ? (
<table>
<thead>
<tr>
<th>ID</th>
<th>Estado</th>
<th>nombre</th>
<th>Fecha</th>
</tr>
</thead>
<tbody>
{companies
.filter((company) =>
filters.length > 0 ? filters.includes(company.status) : true
)
.map((company, i) => (
<tr key={i}>
<td>{company.id}</td>
<td>{company.status}</td>
<td>{company.name}</td>
<td>{company.date}</td>
</tr>
))}
</tbody>
</table>
) : (
<h2>No hay compañías para mostrar</h2>
)}
</div>

sort table based on a certain column

I'm new to both react and Tailwind CSS. I've created a table. Table columns are related (each name in the 1st column has a related mobile number in the 2nd column). I want to add an option on each column of this table, so that when I click on the header of a column, the table rows become sorted (alphabetically or numerically) according to that column. Here is the code:
import React, { useState, useEffect } from 'react'
import { getUsers } from '../../services/userService'
const Table = () => {
const [users, setUsers] = useState([]);
const [currentUsers, setCurrentUsers] = useState([]);
const [search, setSearch] = useState('');
const [isSorted, setIsSorted] = useState(false);
const [sortedUsers, setSortedUsers] = useState([]);
useEffect(async () => {
try {
const response = await getUsers(search);
setUsers(response.data.users);
setPageCount(Math.ceil(response.data.users.length / pageItemCount))
setCurrentUsers(response.data.users.slice(0, pageItemCount))
} catch (error) { }
}, [search]);
const handleChange = (event, value) => {
changePage(value);
}
const sortFn = (userA, userB) => {
// sort logic here, it can be whatever is needed
// sorting alphabetically by `first_name` in this case
return userA.first_name.localeCompare(userB.first_name)
}
const toggleSort = () => {
setIsSorted(!isSorted)
}
// when `currentUsers` changes we want to reset our table
// in order to keep it in sync with actual values
// we're also sorting if we were already sorting
useEffect(() => {
if (isSorted) {
setSortedUsers(currentUsers.slice().sort(sortFn))
} else {
setSortedUsers(currentUsers)
}
}, [isSorted, currentUsers])
return (
<div dir='rtl' className='bg-background mt-10 px-5 rd1200:px-30 overflow-auto'>
<table className='w-full border-separate rounded-md'>
<thead>
<tr className='bg-text-secondary text-white shadow-sm text-center'>
<th className='p-2' onClick={(e) => toggleSort()}>name</th>
<th className='p-2' onClick={(e) => toggleSort()}>mobile</th>
</tr>
</thead>
<tbody>
{sortedUsers.map((item, index) =>
<tr key={item.id} className={index % 2 === 0 ? 'bg-white shadow-sm text-center' : 'bg-text bg-opacity-5 shadow-sm text-center'}>
<td className='text-text text-sm p-2'>{item.first_name}</td>
<td className='text-text text-sm p-2'>{item.mobile}</td>
</tr>
)}
</tbody>
</table>
</div>
)
}
export default Table
The code is working fine. The only problem is that the table is only sorted based on the first name regardless of which column header I click on (So when I click on the mobile column header, the table is still sorted based on the first_name). How can I change it, so that the table content become sorted according to the clicked column header?
You should have some state which holds the key which the data is currently sorted by. Whenever a header is clicked update that state to whatever column was clicked.
On every re-render you can then use the key to access the values to sort by.
Please note: This sort() function will just compare strings and numbers and will only compare if the datatype matches.
const users = [
{ fname: "Thomas", lname: "Fox", age: 51 },
{ fname: "John", lname: "Mayor", age: 18 },
{ fname: "Ronny", lname: "Bush", age: 32 },
{ fname: "Aaron", lname: "Schulz", age: 73 },
];
const cols = [
{ key: "fname", text: "First name" },
{ key: "lname", text: "Last name" },
{ key: "age", text: "Age" },
];
const Table = () => {
const [data, setData] = React.useState(users);
const [columns, setColumns] = React.useState(cols);
const [sortedBy, setSortedBy] = React.useState(columns[0].key);
console.log(`Sorting by column ${sortedBy}`);
const sorted = data.sort((a, b) => {
const aVal = a[sortedBy];
const bVal = b[sortedBy];
if (typeof aVal === "number" && typeof bVal === "number") return aVal - bVal;
else if (typeof aVal === "string" && typeof bVal === "string") return aVal.localeCompare(bVal);
return 1;
});
return (
<table>
<thead>
<tr className="">
{columns.map((col) => (
<th onClick={() => setSortedBy(col.key)}>{col.text}</th>
))}
</tr>
</thead>
<tbody>
{sorted.map((item) => (
<tr>
<td>{item.fname}</td>
<td>{item.lname}</td>
<td>{item.age}</td>
</tr>
))}
</tbody>
</table>
);
};
ReactDOM.render(<Table />, document.getElementById("root"));
th {
border: solid thin;
padding: 0.5rem;
}
table {
border-collapse: collapse;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Instead of just the key you could additionally store whether to store in ascending or descending order for example when we click on a column again we toggle the descending order.
This is just a basic example which should give you some idea on how to implement such functionality.

How can I destructure my array of multiple objects

[
0: {_id: '61de38eb6ea1563609e1d0a7', title: 'FALCON SR SUNTOUR', price: '59', description: ' Alloy.., …}
1: {_id: '61d7a8b885c68311be8dd1b3', title: 'Lifelong LLBC2702 Falcon', price: '59', description: 'Low Maintenance: High.., …}
]
I am creating a react project. I have the above array of objects on my database. If any user place order the order list and user email will go into a single array. So my question is how can I display the orders. I have tried a map also tried a nested map but got " order.map is not a function". Here is the code
{orders.map((order, idx) => order.map(singleOrder => console.log(singleOrder)))}
I want to destructure the order details into UI
This is my code:
const ManageAllOrders = () => {
const [orders, setOrders] = useState([])
const { user } = UseAuth()
useEffect(() => {
fetch(`https://polar-savannah-40370.herokuapp.com/dashboard/orders`)
.then(res => res.json())
.then(data => setOrders(data))
}, [user.email])
console.log('orders', orders);
return (
<div>
<h2>Manage Orders</h2>
<Table responsive striped bordered hover size="sm">
<thead>
<tr>
<th>#</th>
<th>Product Name</th>
<th>Price</th>
<th>Email</th>
<th>Address</th>
<th>Order Date</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{
orders?.map((order, idx) =>
Object.defineProperties(order).map(([key, value]) => {
return (
<div>
{key} : {value}
</div>
);
})
)}
{/* {orders.map((order, idx) => order[0].map(console.log()))} */}
</tbody>
</Table>
</div>
);
};
export default ManageAllOrders;
You cannot call map on each order item as it is an object. To iterate over them use Object.entries method.
Try like below
{
orders?.map((order, idx) =>
Object.entries(order).map(
([key, { description, title, price, cartQuantity }]) => {
if (key !== "_id") {
return (
<tr>
<td>{key}</td>
<td>{title}</td>
<td>{price}</td>
<td>{cartQuantity}</td>
<td>{description}</td>
<td>email</td>
<td>address</td>
</tr>
);
}
}
)
);
}
I also noticed your array has stored values with string keys (email, _id). To retrieve them you can do it like below.
{orders?.["email"]}
{orders?.["_id"]}
Code Sandbox
Is Data type Array??
useEffect(() => {
fetch(`https://polar-savannah-40370.herokuapp.com/dashboard/orders`)
.then(res => res.json())
.then(data => setOrders(data))
}, [user.email])

uploading data to table getting no result

hello iam following mosh hamedani course at some point i got stuck in uploading data in table
this is my table where title and genre is uploading where in stock and rate these are number not string are not uploading here is my table body
class TableBody extends Component {
render() {
const {data,columns} = this.props
console.log({data,columns})
return ( <tbody>
{data.map(item => <tr key={item._id}>
{columns.map(column => <td key={item._id + (column.path || column.key)}>{_.get(item,column.path)}</td>)}
</tr>
)}
</tbody>
);
}
}
data and columns are coming from movietable component here is the code
class MovieTable extends Component {
columns =[
{ path:'title',label:'Title'},
{ path:'genre.name',label:'Genre'},
{ path:'numberInstock',label:'stock'},
{ path:'dailyReantalRate',label:'Rate'},
{ key: 'like' },
{key: 'delete' }
];
render() {
const {movies, onDelete,onSort ,onLike,sortColumn,onAdd,deleted} = this.props;
return (
<table className="table">
<TableHeader columns={this.columns} sortColumn={sortColumn} onSort={onSort}/>
<TableBody data={movies} columns={this.columns}/>
<tbody>
{movies.map((movie) => (
<tr key={movie._id}>
<td>{movie.title}</td>
<td>{movie.genre.name}</td>
<td>{movie.numberInStock}</td>
<td>{movie.dailyRentalRate}</td>
<td>
{" "}
<Like
liked={movie.liked}
onClick={() => onLike(movie)}
/>{" "}
</td>
<td
onClick={() => onDelete(movie._id)}
className="btn btn-danger btn-outline-warning btn-sm active "
>
Remove
</td>
</tr>
))}
</tbody>
<tbody>
{deleted.map((movie) => (
<tr key={movie._id}>
<td>{movie.title}</td>
<td>{movie.genre.name}</td>
<td>{movie.numberInStock}</td>
<td>{movie.dailyRentalRate}</td>
<td>
{" "}
<Like />{" "}
</td>
<td
onClick={() => onAdd (movie._id)}
className="btn btn-danger btn-outline-primary btn-sm active "
>
ADD
</td>
</tr>
))}
</tbody>
</table>
);
}
}
movies from props coming from its parent movies component here is movies component code
class Movies extends Component {
state = {
movies:[],
deleted: [],
genres:[],
pageSize: 9,
currentPage:1,
sortColumn:{
path:'title',
order:'asc'
}
};
componentDidMount(){
const genres =[{ _id:"",name:'All Genres'},...getGenres()]
this.setState({
movies:getMovies(),
genres
})
}
handleDelete = (_id) => {
const movie = this.state.movies.find((x) => x._id === _id);
this.setState({ deleted: [...this.state.deleted, movie] });
this.setState({ movies: this.state.movies.filter((x) => x._id !== _id) });
};
handleLike = (m) => {
const movies = [...this.state.movies];
const index = movies.indexOf(m);
movies[index] = { ...movies[index] };
movies[index].liked = !movies[index].liked;
this.setState({ movies });
};
handleReinstate = (_id) => {
const movie = this.state.deleted.find((movie) => movie._id === _id);
this.setState({ movies: [...this.state.movies, movie] });
this.setState({
deleted: this.state.deleted.filter((movie) => movie._id !== _id),
});
};
handleGenreSelect = genre => {
this.setState({selectedGenre:genre, currentPage:1})
}
handleSort= sortColumn =>{
this.setState({sortColumn});
}
render() {
const { pageSize,currentPage,sortColumn,selectedGenre,movies:allMovies,deleted} = this.state;
const filtered = selectedGenre && selectedGenre._id ? allMovies.filter(m=>m.genre._id === selectedGenre._id ): allMovies;
const sorted = _.orderBy(filtered, [sortColumn.path],[sortColumn.order]);
const movies = paginate(sorted,currentPage,pageSize)
return (
<div className="row">
<div className="col-2">
<ListGroup items={this.state.genres} selectedItem={this.state.selectedGenre} onItemSelect={this.handleGenreSelect}/>
</div>
<div className="col">
<div className={this.getbadgesClasses()}> <p>there are {filtered.length} movies in our data base</p> </div>
<MovieTable
movies={movies}
onSort={this.handleSort}
onDelete={this.handleDelete}
onLike={this.handleLike}
deleted={deleted}
onAdd={this.handleReinstate}/>
<Pagination
itemCount={filtered.length}
pageSize={pageSize}
sortColumn={sortColumn}
onPageChange={this.handlePageChange}
currentPage={currentPage}
/>
</div>
</div>
);
}
getbadgesClasses() {
let classes = " badge m-2 badge-";
classes += this.state.movies.length === 0 ? "warning" : "primary";
return classes;
}
handlePageChange = (page) => {
this.setState({currentPage: page})
};
}
this is my console.log
i have give aerong path to Columns array its like spelling mistake in path

How to delete item seleted in table product

I am trying to delete a product, but it's doesn't show success. I do not know how to get the id of that product to delete
My button onClick = {handleDelete} is import from component in other folder. I try to create handleDelete function, but I missing something in this case.
This is my code for that section
import React, { useState, useEffect } from "react";
import { Container, Row, Col, Table } from "react-bootstrap";
import Loading from "../../components/Loading";
import Button from "../../components/Button/index"
import firebaseApp from "../../api/config";
const ProductTableList = ({
products,
loading,
fetchProductRequest
}) => {
useEffect(() => {
fetchProductRequest();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const firebaseDb = firebaseApp.database();
const [currentId, setCurrentId] = useState("");
if (loading) {
return (
<Container>
<Row>
<Col>
<Loading />
</Col>
</Row>
</Container>
);
}
const handleDelete = (id) => {
const productId = firebaseDb.ref().push().key;
if (window.confirm("Are you sure to delete this record?")) {
firebaseDb
.ref("products")
.child(`products/${productId}`)
.remove((err) => {
if (err) console.log(err);
else setCurrentId("");
});
}
}
const handleUpdate = (event) => {
//TODO
}
return (
<Table striped bordered hover className="product-table">
<thead>
<tr>
<th>No.</th>
<th className="image">Image</th>
<th>Name</th>
<th>Category</th>
<th>Price</th>
<th>Description</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{!!products && products.length > 0 ? (
products.map((product, index) => {
return (
<tr key={index}>
<td>{index}</td>
<td>{product.image}</td>
<td>{product.name}</td>
<td>{product.category}</td>
<td>{product.price}</td>
<td>{product.description}</td>
<td>
<Button onClick={handleDelete} btnText="Delete" />
<Button onClick={handleUpdate} btnText="Update" />
</td>
</tr>
);
})
) :
(
<tr><td className="center-title">Product list is empty!</td></tr>
)}
</tbody>
</Table>
)
}
export default ProductTableList;
Can anyone help me? How do I delete the product that I have selected
Can anyone explain or support for me why? Thank you so much
I made a example, you need to add your function on button click and use your item id to be removed.
import React, { useState, useEffect } from "react";
import { Table } from "react-bootstrap";
const ProductTableList = () => {
const [currentId, setCurrentId] = useState("");
const [products, setProducts] = useState([{
image: 'image',
name: '01',
category: '01',
price: '01',
description: '01'
},
{
image: 'image',
name: '02',
category: '02',
price: '02',
description: '02'
},
{
image: 'image',
name: '03',
category: '03',
price: '03',
description: '03'
}])
const handleDelete = (id) => {
const removeItem = products.filter((item) => item !== products[id])
setProducts(removeItem)
}
return (
<Table striped bordered hover className="product-table">
<thead>
<tr>
<th>No.</th>
<th className="image">Image</th>
<th>Name</th>
<th>Category</th>
<th>Price</th>
<th>Description</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{!!products && products.length > 0 ? (
products.map((product, index) => {
return (
<tr key={index}>
<td>{index}</td>
<td>{product.image}</td>
<td>{product.name}</td>
<td>{product.category}</td>
<td>{product.price}</td>
<td>{product.description}</td>
<td>
<button onClick={() => handleDelete(index)}>Delete</button>
</td>
</tr>
);
})
) :
(
<tr><td className="center-title">Product list is empty!</td></tr>
)}
</tbody>
</Table>
)
}
export default ProductTableList;
Also, avoid index as element key
{ items.map((item, index) => (<li key={index}>{item}</li>)) }
When a list item was added or removed, and the key kept the same, the React assumed that the DOM element had not changed, and the app could not render.
An alternative to cases that the list doesn't have a unique ID is to generate one using shortID.
https://www.npmjs.com/package/shortid

Resources