How do I get the <td> values in a row onClick - reactjs

import React from 'react';
import { Table } from 'reactstrap';
import './SearchDataTable.css'
const SearchDataTable = (props) => {
const serverData = props.serverData
const handleRowClick = (rowValue) => {
console.log(rowValue)
}
return(
<div>
<Table bordered>
<thead>
<tr>
<th>Affected</th>
<th>Type</th>
<th>Quantity</th>
</tr>
</thead>
<tbody>
{props.sortData ? (serverData.sort((a,b) => a.quantity - b.quantity).map(data => (
<tr className="table-row" onClick={(e) => handleRowClick(e)} key={data.quantity}>
<td>{data.affectedOn}</td>
<td>{data.type}</td>
<td>{data.quantity}</td>
</tr>
))):(serverData.sort((a,b) => b.quantity - a.quantity).map(data => (
<tr key={data.quantity}>
<td>{data.affectedOn}</td>
<td>{data.type}</td>
<td>{data.quantity}</td>
</tr>
))) }
</tbody>
</Table>
</div>
)
}
export default SearchDataTable;
From the above code I tried getting the corresponding <td> value onClicking the row. I added the onClick on <tr> and tried, but failed in doing it. I need some help in solving this.

Instead of passing e to handleRowClick, pass the data.
import React from 'react';
import { Table } from 'reactstrap';
import './SearchDataTable.css'
const SearchDataTable = (props) => {
const serverData = props.serverData
const handleRowClick = (rowValue) => {
console.log(rowValue)
}
return(
<div>
<Table bordered>
<thead>
<tr>
<th>Affected</th>
<th>Type</th>
<th>Quantity</th>
</tr>
</thead>
<tbody>
{props.sortData ? (serverData.sort((a,b) => a.quantity - b.quantity).map((data, index) => (
<tr className="table-row" onClick={() => handleRowClick(data)} key={index}>
<td>{data.affectedOn}</td>
<td>{data.type}</td>
<td>{data.quantity}</td>
</tr>
))):(serverData.sort((a,b) => b.quantity - a.quantity).map((data, index) => (
<tr key={index}>
<td>{data.affectedOn}</td>
<td>{data.type}</td>
<td>{data.quantity}</td>
</tr>
))) }
</tbody>
</Table>
</div>
)
}
export default SearchDataTable;
Also replaced the key on the rows with index instead of quanity, since it seems like quantity would not be a unique identifier. Ideally, you would have something else that is unique, like an id property, but index can be okay if not.

const handleRowClick = (data) => {
console.log(data.affectedOn, data.type, data.quantity)
}
....
<tr onClick={() => this.handleClick(data)}>
<td>{data.affectedOn}</td>
<td>{data.type}</td>
<td>{data.quantity}</td>
</tr>
....

Related

Is it possible to dipatsch on useSelector function?

Langage used : JS with REACT REDUX
The context : I have a component who render a list of quotes following the user filter and categories choice.
In my filter component, i store the select value (buttonsData), and here i re render a certains component depending on select value.
import React from 'react';
import { Table } from 'react-bootstrap';
import { useSelector } from 'react-redux';
//here each component following the user choice
import { AllForms } from './categories/AllForms';
import { AtoZ } from './sorted/AtoZ';
import { ZtoA } from './sorted/ZtoA';
import { Ascend } from './sorted/Ascend';
import CurrentOffers from './categories/CurrentOffers';
import ValidateOffers from './categories/ValidateOffers';
export const OfferList = () => {
const buttonsData = useSelector((state) => state.buttonReducer);
return (
<Table hover responsive="md" className="folder__table">
<thead className="folder__content">
<tr className="folder__titles">
<th className="folder__title"> </th>
<th className="folder__title">Order REF</th>
<th
className="folder__title"
>
Entité
</th>
<th className="folder__title">Customer</th>
<th className="folder__title">Status</th>
<th className="folder__title">Date</th>
<th className="folder__title "> </th>
</tr>
</thead>
{buttonsData.activeComponent === 'AllForms' && <AllForms />}
{buttonsData.activeComponent === 'Ascend' && <Ascend />}
{buttonsData.activeComponent === 'validate' && <ValidateOffers />}
</Table>
);
};
I have used createSelector to filter and sort my datas (working fine).
import { useSelector } from 'react-redux';
export const SelectOffersValidate = () => {
//here i select ALL my forms, get with axios
const formsDatas = useSelector((state) => state.offersReducer);
const sortedForms = [...formsDatas].filter(
(oneOffer) => oneOffer.status == 'validate'
);
console.log(sortedForms);
return sortedForms;
};
export const SelectOffersAscend = () => {
const formsDatas = useSelector((state) => state.offersReducer);
const sortedForms = [...formsDatas].sort((a, b) =>
b.createdAt.localeCompare(a.createdAt)
);
return sortedForms;
};
Here a component filtered ( i have one component for AllForms, one for Validate and one for ascend, exaclty the same but with own select function)
import React, { useState } from 'react';
import { FiEdit3 } from 'react-icons/fi';
import {
SelectOffersAscend,
} from '../../../selector/projects.selector.js';
import { isEmpty } from '../../../middlewares/verification.js';
import Moment from 'react-moment';
export const Ascend = () => {
const formsAscend = SelectOffersAscend();
return (
<>
<tbody>
{!isEmpty(formsAscend[0]) &&
formsAscend?.map((oneForm) => {
return (
<tr key={oneForm.id}>
<td>
<input
type="checkbox"
/>
</td>
<td>{oneForm.ref} </td>
<td> {oneForm.entity}</td>
<td>{oneForm.customer} </td>
<td>{oneForm.status} </td>
<td>
<Moment format="DD/MM/YYYY" date={oneForm.createdAt} />
</td>
<td>
<FiEdit3 />
</td>
</tr>
);
})}
</tbody>
</>
);
};
My first problem :
I have made a component for EACH filter, but it's repetitive, is there a better way to do ?
The second problem :
"AllForms" and "ValidateOffers" are categories and "Ascend" is a filter.
For the moment i filter only with AllForms but i would like to filtered based on categories choosen.
I've tried to create an action to store the actual categories, so i've tried to dispatch on my createSelector validate function but it's looping so i don't think is the best way to do
SOLUTION : thanks to Chris whol helped me :)
So i have delete all my filtered component to just have one and create a custom hook
import React, { useMemo } from 'react';
import { Table } from 'react-bootstrap';
import { useSelector } from 'react-redux';
import { OfferRows } from './OfferRows';
export const useFilteredOffers = () => {
const buttonsData = useSelector((state) => state.buttonReducer);
const offersData = useSelector((state) => state.offersReducer);
return useMemo(() => {
switch (buttonsData.activeComponent) {
case 'Ascend': // fix casing
return offersData?.sort((a, b) =>
b.createdAt.localeCompare(a.createdAt)
);
case 'validate':
return offersData?.filter((oneOffer) => oneOffer.status === 'validate');
case 'not validate':
return offersData?.filter(
(oneOffer) => oneOffer.status === 'not validate'
);
case 'AtoZ':
return offersData?.sort((a, b) => a.customer.localeCompare(b.customer));
case 'ZtoA':
return offersData?.sort((a, b) => b.customer.localeCompare(a.customer));
default:
return offersData;
}
}, [buttonsData.activeComponent, offersData]);
};
export const OfferList = () => {
const filteredOffers = useFilteredOffers();
return (
<Table hover responsive="md" className="folder__table">
<thead className="folder__content">
<tr className="folder__titles">
<th className="folder__title"> </th>
<th className="folder__title">Order REF</th>
<th className="folder__title">Entité</th>
<th className="folder__title">Customer</th>
<th className="folder__title">Status</th>
<th className="folder__title">Date</th>
<th className="folder__title "> </th>
</tr>
</thead>
<OfferRows offers={filteredOffers} />
</Table>
);
};
Here the rows
import React from 'react';
import { FiEdit3 } from 'react-icons/fi';
import Moment from 'react-moment';
import { isEmpty } from '../../middlewares/verification.js';
export const OfferRows = ({ offers }) => {
return (
<>
<tbody>
{!isEmpty(offers[0]) &&
offers?.map((oneForm) => {
return (
<tr key={oneForm.id}>
<td>
<input type="checkbox" />
</td>
<td>{oneForm.ref} </td>
<td> {oneForm.entity}</td>
<td>{oneForm.customer} </td>
<td>{oneForm.status} </td>
<td>
<Moment format="DD/MM/YYYY" date={oneForm.createdAt} />
</td>
<td>
<FiEdit3 />
</td>
</tr>
);
})}
</tbody>
</>
);
};
I would create a single component for the rendering of the offer rows. The data can be filtered using a single hook that also selects the active filter. You can also pass this down as an argument.
Custom hooks MUST start with the use keyword. See the Rules of Hooks documentation for more information.
const useFilteredOffers = () => {
const activeFilter = useSelector((state) => state.buttonReducer);
const offers = useSelector((state) => state.offersReducer);
return useMemo(() => {
switch (activeFilter) {
case 'Ascend': // fix casing
return offers?.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
case 'validate':
return offers?.filter(oneOffer => oneOffer.status == 'validate');
default:
return offers;
}
}, [activeFilter, offers]);
}
export const OfferList = () => {
const filteredOffers = useFilteredOffers();
return (
<Table hover responsive="md" className="folder__table">
<thead className="folder__content">
<tr className="folder__titles">
<th className="folder__title"> </th>
<th className="folder__title">Order REF</th>
<th
className="folder__title"
>
Entité
</th>
<th className="folder__title">Customer</th>
<th className="folder__title">Status</th>
<th className="folder__title">Date</th>
<th className="folder__title "> </th>
</tr>
</thead>
<OfferRows offers={filteredOffers} />
</Table>
);
};
For completeness, here is the OfferRows component.
PS: You won't need to use the isEmpty validator because Array#map won't have any effect when the Array is empty.
export const OfferRows = (offers) => {
return (
<>
<tbody>
{offers?.map((oneForm) => {
return (
<tr key={oneForm.id}>
<td>
<input
type="checkbox"
/>
</td>
<td>{oneForm.ref} </td>
<td> {oneForm.entity}</td>
<td>{oneForm.customer} </td>
<td>{oneForm.status} </td>
<td>
<Moment format="DD/MM/YYYY" date={oneForm.createdAt} />
</td>
<td>
<FiEdit3 />
</td>
</tr>
);
})}
</tbody>
</>
);
};

Display a table in React using different components

I'm just starting to learn React and what I'm trying to do is to display a table. I'm doing it using 3 components: App.js, DisplayTable.js, and TableRows.js. The reason I'm doing it this way is that later on, I will need certain rows of the table to be displayed subject to different conditions. I don't get any error messages, but the table is not being displayed. This is my code:
App.js:
import Form from './components/Form'
import { useState } from 'react'
import TableDisplay from './components/TableDisplay'
const App = () => {
const [rows, setRows] = useState([
{
id:1,
description:'',
semester:'',
prefix:'ENG',
number:'368/371',
grade:'',
editing:''
},
{
id:2,
description:'',
semester:'',
prefix:'',
number:'',
grade:'',
editing:''
},
{
id:3,
description:'',
semester:'',
prefix:'',
number:'',
grade:'',
editing:''
},
])
return (
<div className="container">
<Form/>
<TableDisplay rows={rows}/>
</div>
);
}
export default App;
TableDisplay.js:
import TableRows from "./TableRows"
const TableDisplay = ({rows}) => {
return (
<>
{rows.map((row) => {
<TableRows key={row.id} row={row}/>
})}
</>
)
}
export default TableDisplay
TableRows.js:
import React from 'react'
const TableRows = ({row}) => {
return (
<div className="container">
<table className="table table-striped">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Description</th>
<th scope="col">Semester</th>
<th scope="col">Prefix</th>
<th scope="col">Number</th>
<th scope="col">Grade</th>
<th scope="col">Editing</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">{row.id}</th>
<td>{row.description}</td>
<td>{row.semester}</td>
<td>ENG</td>
<td>368/371</td>
<td>{row.grade}</td>
<td><button type="button" className="btn btn-warning">Edit</button></td>
</tr>
<tr>
<th scope="row">{row.id}</th>
<td>{row.description}</td>
<td>{row.semester}</td>
<td>ENG</td>
<td>368/371</td>
<td>{row.grade}</td>
<td><button type="button" className="btn btn-warning">Edit</button></td>
</tr>
<tr>
<th scope="row">{row.id}</th>
<td>{row.description}</td>
<td>{row.semester}</td>
<td>ENG</td>
<td>368/371</td>
<td>{row.grade}</td>
<td><button type="button" className="btn btn-warning">Edit</button></td>
</tr>
</tbody>
</table>
</div>
)
}
export default TableRows
You should remove bracket
const TableDisplay = ({ rows }) => {
return (
<>
{rows.map((row) => <TableRows key={row.id} row={row} />)}
</>
);
};
or add return
const TableDisplay = ({ rows }) => {
return (
<>
{rows.map((row) => {
return <TableRows key={row.id} row={row} />;
})}
</>
);
};

How to store values of selected checkboxes in array in React js?

I make table contain students names, and checkbox for every row in the table, I want so store the students names that checked in the (students) array. How can I do that?
const [data, setData] = useState([]);
const [students, setStudents] = useState([]);
useEffect(() => {
Axios
.get("http://localhost:3003/studentsnotinsections")
.then(result => setData(result.data));
}, []);
console.log(students)
return(
<div>
<table className="table" >
<thead className="thead-dark">
<tr>
<th scope="col">Name</th>
</tr>
</thead>
<tbody>
{data.map((item, id) => {
return <tr key={id}>
<td>{item.FullName}</td>
<td><input type="checkbox" /></td>
</tr>
})}
</tbody>
</table>
</div>
)}
Did you try simply adding onclick function to your checkbox?
const Compon = () => {
const [data, setData] = useState([]);
const [students, setStudents] = useState([]);
useEffect(() => {
Axios
.get("http://localhost:3003/studentsnotinsections")
.then(result => setData(result.data));
}, []);
const addOrRemove = (name) => {
const newStudents = [...students];
const index = newStudents.indexOf(name);
if (index === -1) {
newStudents.push(name);
} else {
newStudents.splice(index, 1);
}
setStudents(newStudents);
console.log(students)
}
return(
<div>
<table className="table" >
<thead className="thead-dark">
<tr>
<th scope="col">Name</th>
</tr>
</thead>
<tbody>
{data.map((item, id) => {
return <tr key={id}>
<td>{item.FullName}</td>
<td><input type="checkbox" onClick={() => addOrRemove(item.FullName)} /></td>
</tr>
})}
</tbody>
</table>
</div>
)
}
You could use onChange handler, it's more standard for input handling that onClick
import { useCallback, useState } from "react";
export default function App() {
const [data, setData] = useState([
{FullName: 'Alex'},
{FullName: 'Richard'}
]);
const [students, setStudents] = useState([]);
let toggleValue = useCallback((event, id) => {
if (event.target.checked) {
setStudents(value => [...value, id])
} else {
setStudents(value => value.filter(it => it !== id))
}
}, [setStudents])
console.log(students)
return(
<div>
<table className="table" >
<thead className="thead-dark">
<tr>
<th scope="col">Name</th>
</tr>
</thead>
<tbody>
{data.map((item, id) => {
return <tr key={id}>
<td>{item.FullName}</td>
<td><input type="checkbox" onChange={(e) => toggleValue(e, id)} /></td>
</tr>
})}
</tbody>
</table>
</div>
)
}

How can I pass props to another components with in reactjs

I'm trying to pass product data from AllProducts component to Product component.
AllProducts.jsx: is showing all the products I have and Product.jsx will show specific product and how can I pass data to Product.jsx?
Here is my AllProducts.jsx:
const AllProducts = (props) => {
const [products, setProducts] = useState([]);
const getProductsAPI = () => {
axios
.get("http://localhost:8000/api/products")
.then((res) => {
setProducts(res.data);
getProductsAPI();
})
.catch((err) => {
console.log(err);
});
};
useEffect(() => {
getProductsAPI();
}, [props]);
return (
<div>
<table className="table table-bordered table-hover">
<thead>
<tr>
<th>#</th>
<th>Title</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{products.map((product, i) => (
<tr key={i}>
<th scope="row">{i}</th>
<td>{product.title}</td>
<td>
<Link to={`/products/${product._id}`}> View </Link>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
and here is my Product.jsx:
const Product = (props) => {
return (
<div className="container">
<h4>{props.product.title}</h4>
</div>
);
};
export default Product;
Here is my project github if you want to look at all the code I have: https://github.com/nathannewyen/full-mern/tree/master/product-manager
If the data is fully loaded for each product in AllProducts, and you don't want to make another API call by product id in the Product component, in this case, you don't have to use a route link to view Product, just make a conditional rendering to show Product component inside AllProducts component. pseudo-code as below,
const [showProduct, setShowProduct] = useState(false);
const [currentProduct, setCurrentProduct] = useState();
const showProduct = (product) => {
setShowProduct(true);
setCurrentProduct(product);
}
<tbody>
{products.map((product, i) => (
<tr key={i}>
<th scope="row">{i}</th>
<td>{product.title}</td>
<td>
<button type="button" onclick = {showProduct(product)}>View</button>
</td>
</tr>
))}
</tbody>
return (showProduct ? <Product /> : <AllProucts/>)
If you also need to make another API call to get extra data for each product, then use the router link but perhaps you can not pass props.

REACT: Instead of sorting by clicking a button - sorting by clicking header "id" in table

I make request to server and I get response. Response it data which I display in view table-list. Also now I try implement when I click button changeAsc happen sort by asc-desc.
But I need that sort by asc-desc was happening when I click on header header id in table. And display the word asc or desc to the right of the header id. Table I export in file Home.js from file - Table.js.
What I need to change in file Table.js that implement sort when I click to header id?
Home.js:
import Table from "./Table/Table.js";
const Home = () => {
const [value, setValue] = useState({
listCategory: [],
sortAscDesc: "asc",
});
useEffect(() => {
async function fetchData(sortAscDesc) {
const res = await api('api/categories', sortAscDesc);
/....
}
fetchData(value.sortAscDesc);
}, [value.sortAscDesc]);
const changeSortAscDesc = () => {
setValue((prev) => ({
...prev,
sortAscDesc: prev.sortAscDesc == 'asc' ? 'desc' : 'asc'
}));
};
return (
<div>
<Table dataAttribute={value.listCategory}/>
// I WANT DELETE THIS BUTTON: - BECAUSE I WANT SORT BY HEADER "id"
<button onClick={() => changeSortAscDesc()}>changeAsc</button>
</div>
);
};
Table.js:
export default ({dataAttribute}) => (
<table className="table">
<thead className="table-head">
<tr>
<th>id</th> //I WANT SORT WHEN I CLICK ELEMENT id
<th>title</th>
<th>created_at</th>
</tr>
</thead>
<tbody>
{dataAttribute.map(item => (
<tr key={item.id}>
<td>{item.id}</td>
<td>{item.title}</td>
<td>{item.created_at}</td>
</tr>
))}
</tbody>
</table>
);
You can try like this:
<Table dataAttribute={value.listCategory} changeSortAscDesc={changeSortAscDesc} />
In your Table.js
export default (props) => (
<table className="table">
<thead className="table-head">
<tr>
<th onClick={props.changeSortAscDesc}>id</th> //I want sort when I click by element id
<th>title</th>
<th>created_at</th>
</tr>
</thead>
<tbody>
{props.dataAttribute.map(item => (
<tr key={item.id}>
<td>{item.id}</td>
<td>{item.title}</td>
<td>{item.created_at}</td>
</tr>
))}
</tbody>
</table>
);

Resources