I have a redux store working in other components, it has nothing strange since I have always connected the components this way.
This is the original code without any redux connected
import React, { useEffect } from "react";
import PropTypes from "prop-types";
import { makeStyles } from "#material-ui/core/styles";
import Table from "#material-ui/core/Table";
import TableBody from "#material-ui/core/TableBody";
import TableCell from "#material-ui/core/TableCell";
import TableContainer from "#material-ui/core/TableContainer";
import TableHead from "#material-ui/core/TableHead";
import TablePagination from "#material-ui/core/TablePagination";
import TableRow from "#material-ui/core/TableRow";
import TableSortLabel from "#material-ui/core/TableSortLabel";
import Paper from "#material-ui/core/Paper";
import InfoPanel from "./InfoPanel.js"
export default function TablePhones(props) {
const rows = props.phones
function descendingComparator(a, b, orderBy) {
if (b[orderBy] < a[orderBy]) {
return -1;
}
if (b[orderBy] > a[orderBy]) {
return 1;
}
return 0;
}
function getComparator(order, orderBy) {
return order === "desc"
? (a, b) => descendingComparator(a, b, orderBy)
: (a, b) => -descendingComparator(a, b, orderBy);
}
function stableSort(array, comparator) {
const stabilizedThis = array.map((el, index) => [el, index]);
stabilizedThis.sort((a, b) => {
const order = comparator(a[0], b[0]);
if (order !== 0) return order;
return a[1] - b[1];
});
return stabilizedThis.map(el => el[0]);
}
const headCells = [
{ id: "name", numeric: false, disablePadding: true, label: "Name" },
{
id: "manufacturer",
numeric: false,
disablePadding: false,
label: "Manufacturer"
},
{ id: "color", numeric: true, disablePadding: false, label: "Color" },
{ id: "price", numeric: true, disablePadding: false, label: "Price" },
{ id: "screen", numeric: true, disablePadding: false, label: "Screen" },
{ id: "processor", numeric: true, disablePadding: false, label: "Processor" },
{ id: "ram", numeric: true, disablePadding: false, label: "Ram" }
];
function EnhancedTableHead(props) {
const { classes, order, orderBy, onRequestSort } = props;
const createSortHandler = property => event => {
onRequestSort(event, property);
};
return (
<TableHead>
<TableRow>
{headCells.map(headCell => (
<TableCell
key={headCell.id}
align={headCell.numeric ? "right" : "left"}
padding={headCell.disablePadding ? "none" : "default"}
sortDirection={orderBy === headCell.id ? order : false}
>
<TableSortLabel
active={orderBy === headCell.id}
direction={orderBy === headCell.id ? order : "asc"}
onClick={createSortHandler(headCell.id)}
>
{headCell.label}
{orderBy === headCell.id ? (
<span className={classes.visuallyHidden}>
{order === "desc" ? "sorted descending" : "sorted ascending"}
</span>
) : null}
</TableSortLabel>
</TableCell>
))}
</TableRow>
</TableHead>
);
}
EnhancedTableHead.propTypes = {
classes: PropTypes.object.isRequired,
numSelected: PropTypes.number.isRequired,
onRequestSort: PropTypes.func.isRequired,
onSelectAllClick: PropTypes.func.isRequired,
order: PropTypes.oneOf(["asc", "desc"]).isRequired,
orderBy: PropTypes.string.isRequired,
rowCount: PropTypes.number.isRequired
};
const useStyles = makeStyles(theme => ({
root: {
width: "100%"
},
paper: {
width: "100%",
marginBottom: theme.spacing(2)
},
table: {
minWidth: 750
},
visuallyHidden: {
border: 0,
clip: "rect(0 0 0 0)",
height: 1,
margin: -1,
overflow: "hidden",
padding: 0,
position: "absolute",
top: 20,
width: 1
}
}));
const classes = useStyles();
const [order, setOrder] = React.useState("asc");
const [orderBy, setOrderBy] = React.useState("calories");
const [page, setPage] = React.useState(0);
const [dense] = React.useState(false);
const [isSelected, setIsSelected] = React.useState(false)
const [rowsPerPage, setRowsPerPage] = React.useState(5);
const handleRequestSort = (event, property) => {
const isAsc = orderBy === property && order === "asc";
setOrder(isAsc ? "desc" : "asc");
setOrderBy(property);
};
const handleClick = (event, name, id, manufacturer, description, color, price, imageFileName, screen, processor, ram) => {
setIsSelected(true)
console.log(name, id, manufacturer, description, color, price, imageFileName, screen, processor, ram)
};
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = event => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0);
};
const emptyRows =
rowsPerPage - Math.min(rowsPerPage, rows.length - page * rowsPerPage);
return (
<div >
<div className={classes.root}>
<Paper className={classes.paper}>
<TableContainer>
<Table
className={classes.table}
aria-labelledby="tableTitle"
size={dense ? "small" : "medium"}
aria-label="enhanced table"
>
<EnhancedTableHead
classes={classes}
order={order}
orderBy={orderBy}
onRequestSort={handleRequestSort}
rowCount={rows.length}
/>
<TableBody>
{stableSort(rows, getComparator(order, orderBy))
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((row, index) => {
const labelId = `enhanced-table-checkbox-${index}`;
return (
<TableRow
hover
onClick={event => handleClick(event, row.name, row.id, row.manufacturer, row.description, row.color, row.price, row.imageFileName, row.screen, row.processor, row.ram)}
role="checkbox"
tabIndex={-1}
key={row.name}
>
<TableCell
component="th"
id={labelId}
scope="row"
padding="none"
>
{row.name}
</TableCell>
<TableCell align="right">{row.manufacturer}</TableCell>
<TableCell align="right">{row.color}</TableCell>
<TableCell align="right">{row.price}</TableCell>
<TableCell align="right">{row.screen}</TableCell>
<TableCell align="right">{row.processor}</TableCell>
<TableCell align="right">{row.ram}</TableCell>
</TableRow>
);
})}
{emptyRows > 0 && (
<TableRow style={{ height: (dense ? 33 : 53) * emptyRows }}>
<TableCell colSpan={6} />
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={[5, 10]}
component="div"
count={rows.length}
rowsPerPage={rowsPerPage}
page={page}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
/>
</Paper>
</div>
{isSelected ? <InfoPanel /> : <div>There is nothing selected</div>}
</div>
);
}
But then I decide to connect it and it suddenly crashes
The only thing I do is:
1: Renaming export default function TablePhones so it looks like function TablePhones
And i add this right in the end of the file
export default connect(mapStateToProps, null)(TablePhones)
I also add this in the beginning of th file
const mapStateToProps = (state) => {
return state
}
So it ends up looking like
import React, { useEffect } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux"
import { makeStyles } from "#material-ui/core/styles";
import Table from "#material-ui/core/Table";
import TableBody from "#material-ui/core/TableBody";
import TableCell from "#material-ui/core/TableCell";
import TableContainer from "#material-ui/core/TableContainer";
import TableHead from "#material-ui/core/TableHead";
import TablePagination from "#material-ui/core/TablePagination";
import TableRow from "#material-ui/core/TableRow";
import TableSortLabel from "#material-ui/core/TableSortLabel";
import Paper from "#material-ui/core/Paper";
import InfoPanel from "./InfoPanel.js"
const mapStateToProps = (state) => {
return state
}
function TablePhones(props) {
const rows = props.phones
function descendingComparator(a, b, orderBy) {
if (b[orderBy] < a[orderBy]) {
return -1;
}
if (b[orderBy] > a[orderBy]) {
return 1;
}
return 0;
}
function getComparator(order, orderBy) {
return order === "desc"
? (a, b) => descendingComparator(a, b, orderBy)
: (a, b) => -descendingComparator(a, b, orderBy);
}
function stableSort(array, comparator) {
const stabilizedThis = array.map((el, index) => [el, index]);
stabilizedThis.sort((a, b) => {
const order = comparator(a[0], b[0]);
if (order !== 0) return order;
return a[1] - b[1];
});
return stabilizedThis.map(el => el[0]);
}
const headCells = [
{ id: "name", numeric: false, disablePadding: true, label: "Name" },
{
id: "manufacturer",
numeric: false,
disablePadding: false,
label: "Manufacturer"
},
{ id: "color", numeric: true, disablePadding: false, label: "Color" },
{ id: "price", numeric: true, disablePadding: false, label: "Price" },
{ id: "screen", numeric: true, disablePadding: false, label: "Screen" },
{ id: "processor", numeric: true, disablePadding: false, label: "Processor" },
{ id: "ram", numeric: true, disablePadding: false, label: "Ram" }
];
function EnhancedTableHead(props) {
const { classes, order, orderBy, onRequestSort } = props;
const createSortHandler = property => event => {
onRequestSort(event, property);
};
return (
<TableHead>
<TableRow>
{headCells.map(headCell => (
<TableCell
key={headCell.id}
align={headCell.numeric ? "right" : "left"}
padding={headCell.disablePadding ? "none" : "default"}
sortDirection={orderBy === headCell.id ? order : false}
>
<TableSortLabel
active={orderBy === headCell.id}
direction={orderBy === headCell.id ? order : "asc"}
onClick={createSortHandler(headCell.id)}
>
{headCell.label}
{orderBy === headCell.id ? (
<span className={classes.visuallyHidden}>
{order === "desc" ? "sorted descending" : "sorted ascending"}
</span>
) : null}
</TableSortLabel>
</TableCell>
))}
</TableRow>
</TableHead>
);
}
EnhancedTableHead.propTypes = {
classes: PropTypes.object.isRequired,
numSelected: PropTypes.number.isRequired,
onRequestSort: PropTypes.func.isRequired,
onSelectAllClick: PropTypes.func.isRequired,
order: PropTypes.oneOf(["asc", "desc"]).isRequired,
orderBy: PropTypes.string.isRequired,
rowCount: PropTypes.number.isRequired
};
const useStyles = makeStyles(theme => ({
root: {
width: "100%"
},
paper: {
width: "100%",
marginBottom: theme.spacing(2)
},
table: {
minWidth: 750
},
visuallyHidden: {
border: 0,
clip: "rect(0 0 0 0)",
height: 1,
margin: -1,
overflow: "hidden",
padding: 0,
position: "absolute",
top: 20,
width: 1
}
}));
const classes = useStyles();
const [order, setOrder] = React.useState("asc");
const [orderBy, setOrderBy] = React.useState("calories");
const [page, setPage] = React.useState(0);
const [dense] = React.useState(false);
const [isSelected, setIsSelected] = React.useState(false)
const [rowsPerPage, setRowsPerPage] = React.useState(5);
const handleRequestSort = (event, property) => {
const isAsc = orderBy === property && order === "asc";
setOrder(isAsc ? "desc" : "asc");
setOrderBy(property);
};
const handleClick = (event, name, id, manufacturer, description, color, price, imageFileName, screen, processor, ram) => {
setIsSelected(true)
console.log(name, id, manufacturer, description, color, price, imageFileName, screen, processor, ram)
};
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = event => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0);
};
const emptyRows =
rowsPerPage - Math.min(rowsPerPage, rows.length - page * rowsPerPage);
return (
<div >
<div className={classes.root}>
<Paper className={classes.paper}>
<TableContainer>
<Table
className={classes.table}
aria-labelledby="tableTitle"
size={dense ? "small" : "medium"}
aria-label="enhanced table"
>
<EnhancedTableHead
classes={classes}
order={order}
orderBy={orderBy}
onRequestSort={handleRequestSort}
rowCount={rows.length}
/>
<TableBody>
{stableSort(rows, getComparator(order, orderBy))
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((row, index) => {
const labelId = `enhanced-table-checkbox-${index}`;
return (
<TableRow
hover
onClick={event => handleClick(event, row.name, row.id, row.manufacturer, row.description, row.color, row.price, row.imageFileName, row.screen, row.processor, row.ram)}
role="checkbox"
tabIndex={-1}
key={row.name}
>
<TableCell
component="th"
id={labelId}
scope="row"
padding="none"
>
{row.name}
</TableCell>
<TableCell align="right">{row.manufacturer}</TableCell>
<TableCell align="right">{row.color}</TableCell>
<TableCell align="right">{row.price}</TableCell>
<TableCell align="right">{row.screen}</TableCell>
<TableCell align="right">{row.processor}</TableCell>
<TableCell align="right">{row.ram}</TableCell>
</TableRow>
);
})}
{emptyRows > 0 && (
<TableRow style={{ height: (dense ? 33 : 53) * emptyRows }}>
<TableCell colSpan={6} />
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={[5, 10]}
component="div"
count={rows.length}
rowsPerPage={rowsPerPage}
page={page}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
/>
</Paper>
</div>
{isSelected ? <InfoPanel /> : <div>There is nothing selected</div>}
</div>
);
}
export default connect(mapStateToProps, null)(TablePhones)
And whenever I try to load it now i get an error
TypeError: array.map is not a function //line 40
Why is this happening, I used to connect it like that, but this makes no sense
Related
I am trying to make a search input that will filter by the name user will input. I have two components one is having the search input (app.js) and the other is having the table(table.js).
I am able to get the current value of the search input (app.js) but when trying to pass it as a props in the table (table.js) it's giving an error.
I am new to react but from my understanding, I think I'm having issues with passing the useState as a props.
1. Code for search input (app.js)
I used onChange event as to get the current value when the input field change.
<div className="search">
<div className={classes.search}>
<div className={classes.searchIcon}>
<SearchIcon />
</div>
<InputBase
placeholder="Search..."
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
onChange={getSearchTerm}
/>
</div>
</div>
getSearchTerm function to get current input value
const [searchTerm, setSearchTerm] = useState("");
const getSearchTerm = (event) => {
const searchWord = event.target.value;
console.log(searchWord);
setSearchTerm(searchWord)
}
2. Table (app.js) passing the props
passing the props in the filter function. So can get filterd when a input would be entered in search input.
export default function EnhancedTable(props) {
console.log("these r props for table component", props);
<TableBody>
{data
.filter((item) => {
if (props.searchTerm == "") {
return item;
} else if (item.clientName.toLowerCase().includes(props.searchTerm.toLowerCase())) {
return item;
}
})
.map((item, index) => {
return (
<TableRow
hover
role="checkbox"
tabIndex={-1}
>
<TableCell padding="checkbox">
<Checkbox
/>
</TableCell>
<TableCell component="th" scope="row" padding="none">{item.clientName}</TableCell>
<TableCell align="right">{item.clientEmail}</TableCell>
<TableCell align="right">{item.clientWorkPhone}</TableCell>
<TableCell align="right">{item.clientIndustry}</TableCell>
<TableCell align="right">{item.tenantId}</TableCell>
<TableCell align="right">{item.clientWebsite}</TableCell>
<TableCell align="right"><Button style={{ backgroundColor: 'transparent', color: '#5900B4' }} variant="outlined" color="primary" href="#outlined-buttons" >{<CreateIcon />}</Button>
</TableCell>
</TableRow>
)
})}
</TableBody>
3. Error getting
[The error I'm getting is here][1]
4. Full Search input (app.js) file for more clarity
function App() {
const [searchTerm, setSearchTerm] = useState("");
const classes = useStyles();
const getSearchTerm = (event) => {
//console.log(inputEl.current.value);
const searchWord = event.target.value;
console.log(searchWord);
setSearchTerm(searchWord)
}
return (
<div className="App">
<div className="wrapper">
<div className="container-table">
<div className="head">
<h5 className='management'>MANAGEMENT</h5>
<div className="head-middle">
<h2>Clients</h2>
<div className="button-collection">
<Button style={{ backgroundColor: '#5900B4', color: '#FFFFFF', fontSize: '15px', fontWeight: '900', width: '206px', height: '42px' }}
variant="contained"
className='add-collection-btn'
startIcon={<AddIcon />}
>
New Collection
</Button>
</div>
</div>
<div className="head-bottom">
<div className="head-button">
<div className="search">
<div className={classes.search}>
<div className={classes.searchIcon}>
<SearchIcon />
</div>
<InputBase
placeholder="Search..."
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
onChange={getSearchTerm}
/>
</div>
</div>
<Button style={{ backgroundColor: 'white', color: 'black', width: '100px', height: '40px', marginLeft: '20px', marginRight: '20px' }} variant="contained">Search</Button>
<Button style={{ backgroundColor: 'white', color: 'black', width: '100px', height: '40px' }} variant="contained">Clear</Button>
</div>
<Button style={{
backgroundColor: 'transparent', color: '#5900B4', width: '206px', height: '42px', borderColor: '#5900B4', fontSize: '15px', fontWeight: '900'
}} variant="outlined" color="primary"
startIcon={<FilterListIcon />}
>
SHOW FILTER
</Button>
</div>
<div className="table">
<EnhancedTable
onChange={setSearchTerm}
/>
</div>
</div>
</div>
</div>
</div>
);
}
export default App;
5. Full table (table.js) file for clarity
const headCells = [
{ id: 'name', numeric: false, disablePadding: true, label: 'Client Name' },
{ id: 'email', numeric: true, disablePadding: false, label: 'Email' },
{ id: 'phone', numeric: true, disablePadding: false, label: 'Phone' },
{ id: 'industry', numeric: true, disablePadding: false, label: 'Industry' },
{ id: 'contact', numeric: true, disablePadding: false, label: 'Point of Contact' },
{ id: 'website', numeric: true, disablePadding: false, label: 'Website' },
{ id: 'btn-icon', numeric: true, disablePadding: false, label: '' },
];
function EnhancedTableHead(props) {
const { classes, onSelectAllClick, order, orderBy, numSelected, rowCount } = props;
return (
<TableHead>
<TableRow style={{ backgroundColor: '#F5F6F8', height: '120px' }}>
<TableCell padding="checkbox">
<Checkbox
indeterminate={numSelected > 0 && numSelected < rowCount}
checked={rowCount > 0 && numSelected === rowCount}
onChange={onSelectAllClick}
inputProps={{ 'aria-label': 'select all desserts' }}
/>
</TableCell>
{headCells.map((headCell) => (
<TableCell
key={headCell.id}
align={headCell.numeric ? 'right' : 'left'}
padding={headCell.disablePadding ? 'none' : 'normal'}
>
{headCell.label}
</TableCell>
))}
</TableRow>
</TableHead>
);
}
EnhancedTableHead.propTypes = {
classes: PropTypes.object.isRequired,
numSelected: PropTypes.number.isRequired,
order: PropTypes.oneOf(['asc', 'desc']).isRequired,
orderBy: PropTypes.string.isRequired,
};
const useToolbarStyles = makeStyles((theme) => ({
root: {
paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(1),
},
highlight:
theme.palette.type === 'light'
? {
color: theme.palette.secondary.main,
backgroundColor: lighten(theme.palette.secondary.light, 0.85),
}
: {
color: theme.palette.text.primary,
backgroundColor: theme.palette.secondary.dark,
},
title: {
flex: '1 1 100%',
},
}));
const EnhancedTableToolbar = (props) => {
const classes = useToolbarStyles();
return (
<Toolbar>
{
<Typography className={classes.title} variant="h6" id="tableTitle" component="div">
Clients
</Typography>
}
</Toolbar>
);
};
EnhancedTableToolbar.propTypes = {
numSelected: PropTypes.number.isRequired,
};
const useStyles = makeStyles((theme) => ({
root: {
width: '100%',
},
paper: {
width: '100%',
marginBottom: theme.spacing(2),
},
table: {
minWidth: 750,
},
visuallyHidden: {
border: 0,
clip: 'rect(0 0 0 0)',
height: 1,
margin: -1,
overflow: 'hidden',
padding: 0,
position: 'absolute',
top: 20,
width: 1,
},
}));
export default function EnhancedTable(props) {
console.log("these r props for table component", props);
const classes = useStyles();
const [order, setOrder] = React.useState('asc');
const [orderBy, setOrderBy] = React.useState('calories');
const [selected, setSelected] = React.useState([]);
const [page, setPage] = React.useState(0);
const [dense, setDense] = React.useState(false);
const [rowsPerPage, setRowsPerPage] = React.useState(5);
const isSelected = (name) => selected.indexOf(name) !== -1;
const [data, setData] = useState([]);
const getData = async () => {
try {
const data = await axios.get("something");
setData(data.data);
} catch (e) {
console.log("this is error for fetching data", e)
}
};
useEffect(() => {
getData();
}, [])
return (
<div className={classes.root}>
<Paper className={classes.paper}>
<EnhancedTableToolbar numSelected={selected.length} />
<TableContainer>
<Table
className={classes.table}
aria-labelledby="tableTitle"
size={dense ? 'small' : 'medium'}
aria-label="enhanced table"
>
<EnhancedTableHead
classes={classes}
numSelected={selected.length}
order={order}
orderBy={orderBy}
/>
<TableBody>
{data
/*.filter((item) => {
if (searchTerm == "") {
return item;
} else if (item.clientName.toLowerCase().includes(searchTerm.toLowerCase())) {
return item;
}
})*/
.map((item, index) => {
return (
<TableRow
hover
role="checkbox"
tabIndex={-1}
>
<TableCell padding="checkbox">
<Checkbox
/>
</TableCell>
<TableCell component="th" scope="row" padding="none">{item.clientName}</TableCell>
<TableCell align="right">{item.clientEmail}</TableCell>
<TableCell align="right">{item.clientWorkPhone}</TableCell>
<TableCell align="right">{item.clientIndustry}</TableCell>
<TableCell align="right">{item.tenantId}</TableCell>
<TableCell align="right">{item.clientWebsite}</TableCell>
<TableCell align="right"><Button style={{ backgroundColor: 'transparent', color: '#5900B4' }} variant="outlined" color="primary" href="#outlined-buttons" >{<CreateIcon />}</Button>
</TableCell>
</TableRow>
)
})}
</TableBody>
</Table>
</TableContainer>
</Paper>
</div>
);
}
The issue is that searchTerm is not within scope of your component so this line...
item.clientName.toLowerCase().includes(searchTerm.toLowerCase())
throws the error you're seeing
Cannot read property 'toLowerCase' of undefined
You need to pass in the required props to your component.
<EnhancedTable searchTerm={searchTerm} />
This is also a perfect opportunity to use React's useMemo hook to produce the filtered results
const { searchTerm } = props // extract searchTerm from props
const filtered = useMemo(() => {
if (!searchTerm) {
return data
}
const term = searchTerm.toLowerCase()
return data.filter(({ clientName }) =>
clientName.toLowerCase().includes(term))
}, [ data, searchTerm ])
Now you can just use filtered in place of data in your return value
<TableBody>
{filtered.map((item, index) => {
// etc
})}
</TableBody>
I have a table that has clickable row headers that would allow the user to sort the table based on the onRequestSort function which you would see below. However this currently is not working correctly, I am using reactHooks to do the handleRequestSort and I may have made a mistake along the way, any advice would be great. Below is my suppliers component
export const desc = (a, b, orderBy) => {
if (b[orderBy] < a[orderBy]) {
return -1;
}
else if (b[orderBy] > a[orderBy]) {
return 1;
}
else {
return 0;
}
};
export const stableSort = (array, cmp) => {
if (array) {
const stabilizedThis = array.map((el, index) => [el, index]);
stabilizedThis.sort((a, b) => {
const order = cmp(a[0], b[0]);
if (order !== 0) return order;
return a[1] - b[1];
});
return stabilizedThis.map(el => el[0]);
}
else return [];
};
export const getSorting = (order, orderBy) => {
return order === 'asc' ? (a, b) => desc(a, b, orderBy) : (a, b) => -
desc(a, b, orderBy);
};
const SuppliersTable = (props) => {
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(5);
const [order, setOrder] = useState('desc');
const [orderBy, setOrderBy] = useState('');
const handleChangePage = (event, page) => {
setPage({ page });
};
const handleChangeRowsPerPage = event => {
setRowsPerPage({ rowsPerPage: event.target.value });
};
const handleRequestSort = (event, property) => {
const orderBy = property;
let order = 'desc';
if (orderBy === property && order === 'desc') {
order = 'asc';
}
setOrder('desc');
setOrderBy('asc');
};
const handleClick = (id) => {
props.history.push(`/suppliers/${id}/details`);
};
const { classes, filteredSuppliers } = props;
const emptyRows = rowsPerPage - Math.min(rowsPerPage, filteredSuppliers.length - page * rowsPerPage);
return (
<Paper className={classes.root}>
<div className={classes.tableWrapper}>
<Table className={classes.table} aria-label="tableTitle" id="suppliersTable">
<SuppliersTableHead
order={order}
orderBy={orderBy}
onRequestSort={handleRequestSort}
rowCount={filteredSuppliers.length}
/>
<TableBody aria-label="suppliers Table">
{stableSort(filteredSuppliers, getSorting(order, orderBy))
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map(supplier => {
return (
supplier.addressInfo.map(row => (
<TableRow className={classes.tableRow}
hover={true}
// onClick={() => { handleClick(supplier.id); }} for a later ticket
tabIndex={0}
key={row.id}
>
<TableCell component="th" scope="row" padding="none">{supplier.name}</TableCell>
<TableCell padding="none">{row.officeName}</TableCell>
<TableCell padding="none">{row.officeEmail}</TableCell>
<TableCell padding="none">{row.officeTelephone}</TableCell>
<TableCell padding="none">{row.town}</TableCell>
<TableCell padding="none">{row.county}</TableCell>
<TableCell padding="none">{row.postcode}</TableCell>
</TableRow>
))
);
})}
{emptyRows > 0 && (
<TableRow style={{ height: 49 * emptyRows }}>
<TableCell colSpan={8} />
</TableRow>
)}
</TableBody>
</Table>
<TablePagination
rowsPerPageOptions={[5, 10, 25]}
component="div"
count={filteredSuppliers.length}
rowsPerPage={rowsPerPage}
page={page}
backIconButtonProps={{
'aria-label': 'Previous Page',
}}
nextIconButtonProps={{
'aria-label': 'Next Page',
}}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
/>
</div>
</Paper>
);
};
and my table head component
const rows = [
{ id: 'title', numeric: false, disablePadding: true, label: 'Title' },
{ id: 'office', numeric: false, disablePadding: true, label: 'Office' },
{ id: 'email', numeric: false, disablePadding: true, label: 'Email' },
{ id: 'telephone', numeric: false, disablePadding: true, label: 'Telephone' },
{ id: 'town', numeric: false, disablePadding: true, label: 'Town' },
{ id: 'county', numeric: false, disablePadding: true, label: 'County' },
{ id: 'postCode', numeric: false, disablePadding: true, label: 'Post Code' },
];
const SuppliersTableHead = props => {
const createSortHandler = property => {
return event => {
props.onRequestSort(event, property);
};
};
const { order, orderBy } = props;
return (
<TableHead>
<TableRow>
{rows.map(row => (
<TableCell
key={row.id}
align={row.numeric ? 'right' : 'left'}
padding={row.disablePadding ? 'none' : 'default'}
sortDirection={orderBy === row.id ? order : false}
>
<Tooltip
title="Sort"
placement={row.numeric ? 'bottom-end' : 'bottom-start'}
enterDelay={300}
>
<TableSortLabel
active={orderBy === row.id}
direction={order}
onClick={createSortHandler(row.id)}
>
{row.label}
</TableSortLabel>
</Tooltip>
</TableCell>
))}
</TableRow>
</TableHead>
);
};
SuppliersTableHead.propTypes = {
onRequestSort: PropTypes.func.isRequired,
order: PropTypes.string.isRequired,
orderBy: PropTypes.string.isRequired,
rowCount: PropTypes.number.isRequired,
};
It looks like you need to pass your orderBy:
<SuppliersTableHead
order={order}
orderBy={orderBy}
onRequestSort={()=>handleRequestSort(orderBy)}
rowCount={filteredSuppliers.length}
/>
const handleRequestSort = (orderBy) => {
// update your sort here
}
I am using django channels to update my React table. The channels work perfectly and sends update to react. With the received update, I update the react state but the table doesn't update or re-render to display added data.
I have console logged the data gotten from the channels and the React state, it updates perfectly, I have also tried using react lifecycles like shouldcomponentupdate and componentdidupdate, yet the state is updating perfectly but the table isn't
This is how I updated the state:
addMessage(message) {
console.log("message")
this.setState({
data: [...this.state.data, message]
})
}
setMessages(messages) {
console.log(messages)
this.setState({ data: messages })
}
And this is a summary of how the table looks
render(){
const {data} = this.state
console.log(data) //shows updated data at every update
return(
<table>
{data.map(n => {
return(
<tr key={n.id}>
<td> {n.name} </td>
<td> {n.found} </td>
<td> {n.date} </td>
</tr>
)})}
</table>
)}
This is the full component as requestd(it's too long that was why I minimized it)
class EnhancedTable extends React.Component {
constructor(props) {
super(props)
this.state = {
order: 'asc',
orderBy: 'calories',
selected: [],
data:[],
message: [],
messages: [],
page: 0,
rowsPerPage: 50,
expanded: false,
search:''
};
this.waitForSocketConnection(() => {
WebSocketInstance.addCallbacks(this.setMessages.bind(this), this.addMessage.bind(this));
WebSocketInstance.newChatMessage(this.props.message)
WebSocketInstance.fetchMessages()
})
}
waitForSocketConnection(callback) {
const component = this;
setTimeout(
function () {
if (WebSocketInstance.state() === 1) {
console.log('connection is secure');
callback()
return;
} else {
console.log('waiting for connection')
component.waitForSocketConnection(callback)
}
}, 100);
}
addMessage(message) {
console.log("message")
let newData = [...this.state.data, message]
this.setState({
data: newData
})
}
setMessages(messages) {
console.log(messages)
this.setState({ data: messages })
}
componentDidMount() {
WebSocketInstance.connect()
}
handleRequestSort = (event, property) => {
const orderBy = property;
let order = 'desc';
if (this.state.orderBy === property && this.state.order === 'desc') {
order = 'asc';
}
this.setState({ order, orderBy });
};
handleSelectAllClick = event => {
if (event.target.checked) {
this.setState(state => ({ selected: state.data.map(n => n.id) }))
return;
}
this.setState({ selected: [] });
};
handleAddClick = () => event => {
const selected = this.state.selected
selected.forEach(function (element) {
axios.put(`${process.env.REACT_APP_DEV_API_URL}/api/main/${element}/`, {
relevance: true
})
.then((response) =>
console.log(response)
)
.catch(function (error) {
console.log(error)
})
}
)
}
handleClick = (event, id) => {
const { selected } = this.state;
const selectedIndex = selected.indexOf(id);
let newSelected = [];
if (selectedIndex === -1) {
newSelected = newSelected.concat(selected, id);
} else if (selectedIndex === 0) {
newSelected = newSelected.concat(selected.slice(1));
} else if (selectedIndex === selected.length - 1) {
newSelected = newSelected.concat(selected.slice(0, -1));
} else if (selectedIndex > 0) {
newSelected = newSelected.concat(
selected.slice(0, selectedIndex),
selected.slice(selectedIndex + 1),
);
}
this.setState({ selected: newSelected });
};
handleChangePage = (event, page) => {
this.setState({ page });
};
handleChangeRowsPerPage = event => {
this.setState({ rowsPerPage: event.target.value });
};
handleChange = panel => (event, expanded) => {
this.setState({
expanded: expanded ? panel : false,
});
};
isSelected = id => this.state.selected.indexOf(id) !== -1;
render() {
const { classes } = this.props;
const { order, orderBy, selected, rowsPerPage, page, data, expanded } = this.state;
const emptyRows = rowsPerPage - Math.min(rowsPerPage, data.length - page * rowsPerPage);
// const { expanded } = this.state;
console.log(data)
return (
<Paper className={classes.root}>
<EnhancedTableToolbar numSelected={selected.length} handleAddClick={this.handleAddClick()} />
<div className={classes.tableWrapper}>
<Table className={classes.table} aria-labelledby="tableTitle">
<EnhancedTableHead
numSelected={selected.length}
order={order}
orderBy={orderBy}
onSelectAllClick={this.handleSelectAllClick}
onRequestSort={this.handleRequestSort}
rowCount={data.length}
/>
<TableBody>
{stableSort(data, getSorting(order, orderBy))
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map(n => {
const isSelected = this.isSelected(n.id);
return (
<TableRow
hover
key={n.id}
>
<TableCell padding="checkbox" onClick={event => this.handleClick(event, n.id)}
role="checkbox"
aria-checked={isSelected}
tabIndex={-1}
key={n.id}
selected={isSelected}>
<Checkbox checked={n.relevance === true ? true : false} classes={{
root: classes.root,
}} />
</TableCell>
<TableCell component="th" scope="row" padding="none" style={{ color: "#1565C0" }}>
<a href={`${n.url}`} target="_blank" rel='noopener noreferrer'>{expanded === false ? `${n.url.slice(0, 30)}` : `${n.url}`}</a>
</TableCell>
<TableCell component="th" scope="row" padding="none">{n.keyword}</TableCell>
<TableCell component="th" scope="row" padding="none"><ExpansionPanel
square
expanded={expanded === `${n.id}`}
onChange={this.handleChange(`${n.id}`)}
>
<ExpansionPanelSummary>
<Typography style={{ color: `${n.keyword ? 'red' : 'none'}` }}>{n.source.slice(1, 100)}...</Typography> <Button color="primary" style={{ padding: '0px' }}>{expanded === false ? 'Read More' : 'Read Less'}</Button>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<div>
{n.source.split(',').map(element => {
return (<Typography key={Math.random()} >{element.replace(n.keyword, n.keyword.toUpperCase())}</Typography>)
})}
</div>
</ExpansionPanelDetails>
</ExpansionPanel>
</TableCell>
<TableCell align="right">{n.found}</TableCell>
<TableCell align="right">
{n.severityLevel === 'low' ?
<Badge className={classes.margin} badgeContent={"Low"} color="primary">
<CloudCircle />
</Badge>
:
<Badge className={classes.margin} badgeContent={"High"} color="secondary">
<AddCircleIcon />
</Badge>}
</TableCell>
<TableCell align="right" style={{ color: "#2E7D32" }}>{n.timestamp}</TableCell>
</TableRow>
);
})}
{emptyRows > 0 && (
<TableRow style={{ height: 49 * emptyRows }}>
<TableCell colSpan={6} />
</TableRow>
)}
</TableBody>
</Table>
</div>
<TablePagination
rowsPerPageOptions={[5, 10, 25]}
component="div"
count={data.length}
rowsPerPage={rowsPerPage}
page={page}
backIconButtonProps={{
'aria-label': 'Previous Page',
}}
nextIconButtonProps={{
'aria-label': 'Next Page',
}}
onChangePage={this.handleChangePage}
onChangeRowsPerPage={this.handleChangeRowsPerPage}
/>
</Paper>
);
}
}
EnhancedTable.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(EnhancedTable);
I expected as the state updates, the table updates and shows updated data.
Are you using the key prop on your loops children? When you make loops in your render function you need to provide the key prop to the loops child so React knows what components to update.
So in your case it would be something like this: {data.map(n => <Component key={n.id} />)} if n is an object with an unique property id.
React key prop: https://reactjs.org/docs/lists-and-keys.html#keys
I was able to fix this using redux and react lifecycle method,
shouldComponentUpdate(nextProps, nextState){
if (this.state.data !== nextState.data){
this.setState({
data: nextState.data
})
return true
}
return false
}
This compares the current state and the next state.
I m using Material UI and following this https://material-ui.com/demos/tables/ sample.
When i try to do sample for Custom Table Pagination Action it's not working. It's returning inherits.js:5 Uncaught TypeError: Super expression must either be null or a function error.
I narrow down to issue, when i add the below import i could see the above error.
import TablePagination from '#material-ui/core/TablePagination';
Please find the code below and suggest me to move on.
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import Table from '#material-ui/core/Table';
import TableBody from '#material-ui/core/TableBody';
import TableCell from '#material-ui/core/TableCell';
import TableFooter from '#material-ui/core/TableFooter';
import TablePagination from '#material-ui/core/TablePagination';
import TableRow from '#material-ui/core/TableRow';
import Paper from '#material-ui/core/Paper';
import IconButton from '#material-ui/core/IconButton';
import FirstPageIcon from '#material-ui/icons/FirstPage';
import KeyboardArrowLeft from '#material-ui/icons/KeyboardArrowLeft';
import KeyboardArrowRight from '#material-ui/icons/KeyboardArrowRight';
import LastPageIcon from '#material-ui/icons/LastPage';
const actionsStyles = theme => ({
root: {
flexShrink: 0,
color: theme.palette.text.secondary,
marginLeft: theme.spacing.unit * 2.5,
},
});
class TablePaginationActions extends React.Component {
handleFirstPageButtonClick = event => {
this.props.onChangePage(event, 0);
};
handleBackButtonClick = event => {
this.props.onChangePage(event, this.props.page - 1);
};
handleNextButtonClick = event => {
this.props.onChangePage(event, this.props.page + 1);
};
handleLastPageButtonClick = event => {
this.props.onChangePage(
event,
Math.max(0, Math.ceil(this.props.count / this.props.rowsPerPage) - 1),
);
};
render() {
const { classes, count, page, rowsPerPage, theme } = this.props;
return (
<div className={classes.root}>
<IconButton
onClick={this.handleFirstPageButtonClick}
disabled={page === 0}
aria-label="First Page"
>
{theme.direction === 'rtl' ? <LastPageIcon /> : <FirstPageIcon />}
</IconButton>
<IconButton
onClick={this.handleBackButtonClick}
disabled={page === 0}
aria-label="Previous Page"
>
{theme.direction === 'rtl' ? <KeyboardArrowRight /> : <KeyboardArrowLeft />}
</IconButton>
<IconButton
onClick={this.handleNextButtonClick}
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
aria-label="Next Page"
>
{theme.direction === 'rtl' ? <KeyboardArrowLeft /> : <KeyboardArrowRight />}
</IconButton>
<IconButton
onClick={this.handleLastPageButtonClick}
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
aria-label="Last Page"
>
{theme.direction === 'rtl' ? <FirstPageIcon /> : <LastPageIcon />}
</IconButton>
</div>
);
}
}
TablePaginationActions.propTypes = {
classes: PropTypes.object.isRequired,
count: PropTypes.number.isRequired,
onChangePage: PropTypes.func.isRequired,
page: PropTypes.number.isRequired,
rowsPerPage: PropTypes.number.isRequired,
theme: PropTypes.object.isRequired,
};
const TablePaginationActionsWrapped = withStyles(actionsStyles, { withTheme: true })(
TablePaginationActions,
);
let counter = 0;
function createData(name, calories, fat) {
counter += 1;
return { id: counter, name, calories, fat };
}
const styles = theme => ({
root: {
width: '100%',
marginTop: theme.spacing.unit * 3,
},
table: {
minWidth: 500,
},
tableWrapper: {
overflowX: 'auto',
},
});
class CustomPaginationActionsTable extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [
createData('Cupcake', 305, 3.7),
createData('Donut', 452, 25.0),
createData('Eclair', 262, 16.0),
createData('Frozen yoghurt', 159, 6.0),
createData('Gingerbread', 356, 16.0),
createData('Honeycomb', 408, 3.2),
createData('Ice cream sandwich', 237, 9.0),
createData('Jelly Bean', 375, 0.0),
createData('KitKat', 518, 26.0),
createData('Lollipop', 392, 0.2),
createData('Marshmallow', 318, 0),
createData('Nougat', 360, 19.0),
createData('Oreo', 437, 18.0),
].sort((a, b) => (a.calories < b.calories ? -1 : 1)),
page: 0,
rowsPerPage: 5,
};
}
handleChangePage = (event, page) => {
this.setState({ page });
};
handleChangeRowsPerPage = event => {
this.setState({ rowsPerPage: event.target.value });
};
render() {
const { classes } = this.props;
const { data, rowsPerPage, page } = this.state;
const emptyRows = rowsPerPage - Math.min(rowsPerPage, data.length - page * rowsPerPage);
return (
<Paper className={classes.root}>
<div className={classes.tableWrapper}>
<Table className={classes.table}>
<TableBody>
{data.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map(n => {
return (
<TableRow key={n.id}>
<TableCell component="th" scope="row">
{n.name}
</TableCell>
<TableCell numeric>{n.calories}</TableCell>
<TableCell numeric>{n.fat}</TableCell>
</TableRow>
);
})}
{emptyRows > 0 && (
<TableRow style={{ height: 48 * emptyRows }}>
<TableCell colSpan={6} />
</TableRow>
)}
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
colSpan={3}
count={data.length}
rowsPerPage={rowsPerPage}
page={page}
onChangePage={this.handleChangePage}
onChangeRowsPerPage={this.handleChangeRowsPerPage}
ActionsComponent={TablePaginationActionsWrapped}
/>
</TableRow>
</TableFooter>
</Table>
</div>
</Paper>
);
}
}
CustomPaginationActionsTable.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(CustomPaginationActionsTable);
I think it might be related to this this post right here which talks about circular dependencies. Check your version compatibility.
My design currently stands like this
I am using Material UI's radio button, i want to make each row select able just once. When i add a <RadioButton> component, i am able to select it but however i am not able to toggle between rows
transactionRow(member: Object) {
return (
<tr id='drawLotteryTabel' style={styles.tr} key={member.uuid}>
<td className="col-md-2 col-xs-2">{member.user.fullName}</td>
<td className="col-md-1 col-xs-1">{this.getSubscriptionDropDown(member.subscriptions)}</td>
<td className="col-md-2 col-xs-2">{CommonConstants.INR_SYMBOL + ' ' + Utils.formatNumberLocalised(member.bidDiscountAmount || 0)}</td>
<td className="col-md-2 col-xs-2">{CommonConstants.INR_SYMBOL + ' ' + Utils.formatNumberLocalised(member.bidDiscountPercent || 0)}</td>
<td className="col-md-2 col-xs-2">{CommonConstants.INR_SYMBOL + ' ' + Utils.formatNumberLocalised(member.unpaidAmount || 0)}</td>
<td
className="col-md-2 col-xs-2"
>
<RadioButtonGroup name="shipSpeed" defaultSelected="not_light" key={member.uuid}>
<RadioButton
value="light"
style={styles.radioButton}
/>
</RadioButtonGroup></td>
</tr>
);
}
The Above code results in something like this
What should i do so that i get the entire table row as a single entity to select.
You'll probably, unfortunately, need to manage the state of your radio buttons manually, as a controlled component.
Have the RadioButtonGroup include a call to an onChange function that stores the selected value:
handleChange: (event, value) => {
this.setState({selectedButtonValue: value});
}
And then push that value to every RadioButtonGroup using the valueSelected property as such;
<RadioButtonGroup name="shipSpeed" defaultSelected="not_light" key={member.uuid}
onChange={this.handleChange}
valueSelected={this.state.selectedButtonValue}
>
I assume you are using "Enhanced Table" from material ui.
If that's the case
replace
if (selectedIndex === -1) {
newSelected = newSelected.concat(selected, name);
} else if (selectedIndex === 0) {
newSelected = newSelected.concat(selected.slice(1));
} else if (selectedIndex === selected.length - 1) {
newSelected = newSelected.concat(selected.slice(0, -1));
} else if (selectedIndex > 0) {
newSelected = newSelected.concat(
selected.slice(0, selectedIndex),
selected.slice(selectedIndex + 1),
);
}
with
if (selectedIndex === -1) {
newSelected = [name];
}
and replace
The Checkbox component with Radio component.
change this
<TableCell padding="checkbox">
<Checkbox
checked={isItemSelected}
inputProps={{ 'aria-labelledby': labelId }}
/>
</TableCell>
to this
<TableCell padding="checkbox">
<Radio
checked={isItemSelected}
inputProps={{ 'aria-labelledby': labelId }}
/>
</TableCell>
You should be able to toggle between the radio buttons as the button only activates when new row is selected and it replaces the old selected state.
I am attaching the implementation of material ui enhanced table with radio button implementation. I did some tweak here and there. You can take a look.
import React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { lighten, makeStyles } from '#material-ui/core/styles';
import Table from '#material-ui/core/Table';
import TableBody from '#material-ui/core/TableBody';
import TableCell from '#material-ui/core/TableCell';
import TableContainer from '#material-ui/core/TableContainer';
import TableHead from '#material-ui/core/TableHead';
import TablePagination from '#material-ui/core/TablePagination';
import TableRow from '#material-ui/core/TableRow';
import TableSortLabel from '#material-ui/core/TableSortLabel';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import Paper from '#material-ui/core/Paper';
import Radio from '#material-ui/core/Radio';
// import IconButton from '#material-ui/core/IconButton';
// import Tooltip from '#material-ui/core/Tooltip';
// import FormControlLabel from '#material-ui/core/FormControlLabel';
// import Switch from '#material-ui/core/Switch';
// import DeleteIcon from '#material-ui/icons/Delete';
// import FilterListIcon from '#material-ui/icons/FilterList';
function createData(name, calories, fat, carbs, protein) {
return { name, calories, fat, carbs, protein };
}
const rows = [
createData('Cupcake', 305, 3.7, 67, 4.3),
createData('Donut', 452, 25.0, 51, 4.9),
createData('Eclair', 262, 16.0, 24, 6.0),
createData('Frozen yoghurt', 159, 6.0, 24, 4.0),
createData('Gingerbread', 356, 16.0, 49, 3.9),
createData('Honeycomb', 408, 3.2, 87, 6.5),
createData('Ice cream sandwich', 237, 9.0, 37, 4.3),
createData('Jelly Bean', 375, 0.0, 94, 0.0),
createData('KitKat', 518, 26.0, 65, 7.0),
createData('Lollipop', 392, 0.2, 98, 0.0),
createData('Marshmallow', 318, 0, 81, 2.0),
createData('Nougat', 360, 19.0, 9, 37.0),
createData('Oreo', 437, 18.0, 63, 4.0),
];
function descendingComparator(a, b, orderBy) {
if (b[orderBy] < a[orderBy]) {
return -1;
}
if (b[orderBy] > a[orderBy]) {
return 1;
}
return 0;
}
function getComparator(order, orderBy) {
return order === 'desc'
? (a, b) => descendingComparator(a, b, orderBy)
: (a, b) => -descendingComparator(a, b, orderBy);
}
function stableSort(array, comparator) {
const stabilizedThis = array.map((el, index) => [el, index]);
stabilizedThis.sort((a, b) => {
const order = comparator(a[0], b[0]);
if (order !== 0) return order;
return a[1] - b[1];
});
return stabilizedThis.map((el) => el[0]);
}
const headCells = [
{ id: 'name', numeric: false, disablePadding: true, label: 'Dessert (100g serving)' },
{ id: 'calories', numeric: true, disablePadding: false, label: 'Calories' },
{ id: 'fat', numeric: true, disablePadding: false, label: 'Fat (g)' },
{ id: 'carbs', numeric: true, disablePadding: false, label: 'Carbs (g)' },
{ id: 'protein', numeric: true, disablePadding: false, label: 'Protein (g)' },
];
function EnhancedTableHead(props) {
const { classes, order, orderBy, onRequestSort } = props;
const createSortHandler = (property) => (event) => {
onRequestSort(event, property);
};
return (
<TableHead>
<TableRow>
<TableCell padding="checkbox">
</TableCell>
{headCells.map((headCell) => (
<TableCell
key={headCell.id}
align={headCell.numeric ? 'right' : 'left'}
padding={headCell.disablePadding ? 'none' : 'default'}
sortDirection={orderBy === headCell.id ? order : false}
>
<TableSortLabel
active={orderBy === headCell.id}
direction={orderBy === headCell.id ? order : 'asc'}
onClick={createSortHandler(headCell.id)}
>
{headCell.label}
{orderBy === headCell.id ? (
<span className={classes.visuallyHidden}>
{order === 'desc' ? 'sorted descending' : 'sorted ascending'}
</span>
) : null}
</TableSortLabel>
</TableCell>
))}
</TableRow>
</TableHead>
);
}
EnhancedTableHead.propTypes = {
classes: PropTypes.object.isRequired,
onRequestSort: PropTypes.func.isRequired,
onSelectAllClick: PropTypes.func.isRequired,
order: PropTypes.oneOf(['asc', 'desc']).isRequired,
orderBy: PropTypes.string.isRequired,
rowCount: PropTypes.number.isRequired,
};
const useToolbarStyles = makeStyles((theme) => ({
root: {
paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(1),
},
highlight:
theme.palette.type === 'light'
? {
color: theme.palette.secondary.main,
backgroundColor: lighten(theme.palette.secondary.light, 0.85),
}
: {
color: theme.palette.text.primary,
backgroundColor: theme.palette.secondary.dark,
},
title: {
flex: '1 1 100%',
},
}));
const EnhancedTableToolbar = (props) => {
const classes = useToolbarStyles();
const { numSelected } = props;
return (
<Toolbar
className={clsx(classes.root, {
[classes.highlight]: numSelected.length > 0,
})}
>
{numSelected.length > 0 ? (
<Typography className={classes.title} color="inherit" variant="subtitle1" component="div">
{numSelected}
</Typography>
) : (
<Typography className={classes.title} variant="h6" id="tableTitle" component="div">
Nutrition
</Typography>
)}
{/* {numSelected.length > 0 ? (
<Tooltip title="Delete">
<IconButton aria-label="delete">
<DeleteIcon />
</IconButton>
</Tooltip>
) : (
<Tooltip title="Filter list">
<IconButton aria-label="filter list">
<FilterListIcon />
</IconButton>
</Tooltip>
)} */}
</Toolbar>
);
};
EnhancedTableToolbar.propTypes = {
numSelected: PropTypes.string.isRequired,
};
const useStyles = makeStyles((theme) => ({
root: {
width: '100%',
},
paper: {
width: '100%',
marginBottom: theme.spacing(2),
},
table: {
minWidth: 750,
},
visuallyHidden: {
border: 0,
clip: 'rect(0 0 0 0)',
height: 1,
margin: -1,
overflow: 'hidden',
padding: 0,
position: 'absolute',
top: 20,
width: 1,
},
}));
export default function EnhancedTableWithRadio() {
const classes = useStyles();
const [order, setOrder] = React.useState('asc');
const [orderBy, setOrderBy] = React.useState('calories');
const [selected, setSelected] = React.useState('');
const [page, setPage] = React.useState(0);
// const [dense, setDense] = React.useState(false);
const [rowsPerPage, setRowsPerPage] = React.useState(5);
const handleRequestSort = (event, property) => {
const isAsc = orderBy === property && order === 'asc';
setOrder(isAsc ? 'desc' : 'asc');
setOrderBy(property);
};
const handleClick = (event, name) => {
let newSelected = selected;
if (name !== selected) {
newSelected = name;
}
setSelected(newSelected);
};
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0);
};
// const handleChangeDense = (event) => {
// setDense(event.target.checked);
// };
const isSelected = (name) => selected.indexOf(name) !== -1;
const emptyRows = rowsPerPage - Math.min(rowsPerPage, rows.length - page * rowsPerPage);
return (
<div className={classes.root}>
<Paper className={classes.paper}>
<EnhancedTableToolbar numSelected={selected} />
<TableContainer>
<Table
className={classes.table}
aria-labelledby="tableTitle"
// size={dense ? 'small' : 'medium'}
size = "medium"
aria-label="enhanced table"
>
<EnhancedTableHead
classes={classes}
order={order}
orderBy={orderBy}
onRequestSort={handleRequestSort}
rowCount={rows.length}
/>
<TableBody>
{stableSort(rows, getComparator(order, orderBy))
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((row, index) => {
const isItemSelected = isSelected(row.name);
const labelId = `enhanced-table-checkbox-${index}`;
return (
<TableRow
hover
onClick={(event) => handleClick(event, row.name)}
role="checkbox"
aria-checked={isItemSelected}
tabIndex={-1}
key={row.name}
selected={isItemSelected}
>
<TableCell padding="checkbox">
<Radio
checked={isItemSelected}
inputProps={{ 'aria-labelledby': labelId }}
/>
</TableCell>
<TableCell component="th" id={labelId} scope="row" padding="none">
{row.name}
</TableCell>
<TableCell align="right">{row.calories}</TableCell>
<TableCell align="right">{row.fat}</TableCell>
<TableCell align="right">{row.carbs}</TableCell>
<TableCell align="right">{row.protein}</TableCell>
</TableRow>
);
})}
{emptyRows > 0 && (
<TableRow style={{ height: 53 * emptyRows }}>
{/* <TableRow style={{ height: (dense ? 33 : 53) * emptyRows }}> */}
<TableCell colSpan={6} />
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={[5, 10]}
component="div"
count={rows.length}
rowsPerPage={rowsPerPage}
page={page}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
/>
</Paper>
{/* <FormControlLabel
control={<Switch checked={dense} onChange={handleChangeDense} />}
label="Dense padding"
/> */}
</div>
);
}
I had the same problem using a Radio button component inside a cell from a Table, both components from MaterialUi. I resolve like this:
This is my handleChange function to select de value of the Row.id that i use for my app
handleChange(field, event) {
this.setState({[field] : event.target.value});
}
And in my Table component i just use the "Radio" component without a "RadioButtonGoup" component. And use a condition in the "checked" prop of the "Radio" component to show it as checked or not.
<Table aria-label="simple table">
<TableHead>
<TableRow>
<TableCell align="center">Selecciona</TableCell>
<TableCell>Brand</TableCell>
<TableCell align="center">Tarjetahabiente</TableCell>
<TableCell align="center">Terminación</TableCell>
<TableCell align="center">Expira</TableCell>
<TableCell align="center">Eliminar</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map(row => (
<TableRow key={row.id}>
<TableCell component="th" scope="row">
<Radio
value={row.id}
defaultSelected={false}
checked={row.id != this.state.paymentSourceId ? false : true}
onChange={this.handleChange.bind(this, 'paymentSourceId')}
/>
</TableCell>
<TableCell align="center">{row.brand}</TableCell>
<TableCell align="center">{row.name}</TableCell>
<TableCell align="center">{row.last4}</TableCell>
<TableCell align="center">{row.exp_month}/{row.exp_year}</TableCell>
<TableCell align="center">
<IconButton
aria-label="delete"
color="primary"
onClick={()=>this.handleDelete(this.state.paymentSourceId)}>
<DeleteIcon />
</IconButton>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>