When I am selecting any Row the checkboxes aren't getting highlighted. Sometimes even if I selected any row then every rows gets selected automatically. I am using Material UI Table in ReactJs
const headCells = [
{
id: "sl_no",
numeric: true,
disablePadding: false,
label: "Sl_no",
},
{
id: "business_code",
numeric: false,
disablePadding: false,
label: "Business_Code",
},
//removed most columns soo as to post the question easily but can refer to image for the column names
function EnhancedTableHead(props) {
const {
onSelectAllClick,
order,
orderBy,
numSelected,
rowCount,
onRequestSort,
} = props;
const createSortHandler = (property) => (event) => {
onRequestSort(event, property);
};
return (
<TableHead>
<TableRow>
<TableCell padding="checkbox">
<Checkbox
color="primary"
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"}
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 ? (
<Box component="span" sx={visuallyHidden}>
{order === "desc" ? "sorted descending" : "sorted ascending"}
</Box>
) : null}
</TableSortLabel>
</TableCell>
))}
</TableRow>
</TableHead>
);
}
export default function EnhancedTable() {
const [selected, setSelected] = React.useState([]);
const handleSelectAllClick = (event) => {
if (event.target.checked) {
const newSelecteds = rows.map((n) => n.name);
setSelected(newSelecteds);
return;
}
setSelected([]);
};
const handleClick = (event, name) => {
const selectedIndex = selected.indexOf(name);
let newSelected = [];
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)
);
}
setSelected(newSelected);
};
const isSelected = (name) => selected.indexOf(name) !== -1;
// Avoid a layout jump when reaching the last page with empty rows.
const emptyRows =
page > 0 ? Math.max(0, (1 + page) * rowsPerPage - rows.length) : 0;
return (
<Box sx={{ width: "100%" }}>
<Paper sx={{ width: "100%", mb: 2 }}>
<EnhancedTableToolbar numSelected={selected.length} />
<TableContainer>
<Table
sx={{ minWidth: 750 }}
aria-labelledby="tableTitle"
size={dense ? "small" : "medium"}
>
<EnhancedTableHead
numSelected={selected.length}
order={order}
orderBy={orderBy}
onSelectAllClick={handleSelectAllClick}
onRequestSort={handleRequestSort}
rowCount={rows.length}
/>
<TableBody>
rows.slice().sort(getComparator(order, orderBy)) */}
{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.sl_no)}
role="checkbox"
aria-checked={isItemSelected}
tabIndex={-1}
key={row.sl_no}
selected={isItemSelected}
>
<TableCell padding="checkbox">
<Checkbox
color="primary"
checked={isItemSelected}
inputProps={{
"aria-labelledby": labelId,
}}
/>
</TableCell>
<TableCell
component="th"
id={labelId}
scope="row"
padding="none"
>
{row.sl_no}
</TableCell>
<TableCell align="center">{row.business_code}</TableCell>
</TableRow>
);
})}
{emptyRows > 0 && (
<TableRow
style={{
height: (dense ? 33 : 53) * emptyRows,
}}
>
<TableCell colSpan={6} />
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
</Paper>
</Box>
);
}
This is how the the invoice table looks
Related
I'm working with MUI table components. I have made the below table example with pagination.
const MuiTable = () => {
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(5);
const [data, setData] = useState([]);
useEffect(() => {
setData(tableData);
}, []);
const emptyRows =
page > 0 ? Math.max(0, (1 + page) * rowsPerPage - data.length) : 0;
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0);
};
return (
<TableContainer sx={{ maxHeight: "300px" }} component={Paper}>
<Table stickyHeader aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Id</TableCell>
<TableCell>First Name</TableCell>
<TableCell>Last Name</TableCell>
<TableCell align="center">Email</TableCell>
</TableRow>
</TableHead>
<TableBody>
{(rowsPerPage > 0
? data.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
: data
).map((row) => (
<TableRow
key={row.id}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell>{row.id}</TableCell>
<TableCell>{row.first_name}</TableCell>
<TableCell>{row.last_name}</TableCell>
<TableCell align="center">{row.email}</TableCell>
</TableRow>
))}
{emptyRows > 0 && (
<TableRow style={{ height: 53 * emptyRows }}>
<TableCell colSpan={6} />
</TableRow>
)}
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={[5, 10, 25]}
count={data.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
SelectProps={{
inputProps: {
"aria-label": "rows per page",
},
}}
labelDisplayedRows={({ page }) => {
return `Page: ${page}`;
}}
backIconButtonProps={{
color: "secondary",
}}
nextIconButtonProps={{ color: "secondary" }}
showFirstButton={true}
showLastButton={true}
labelRowsPerPage={<span>Rows:</span>}
sx={{
".MuiTablePagination-toolbar": {
backgroundColor: "rgba(100,100,100,0.5)",
},
".MuiTablePagination-selectLabel, .MuiTablePagination-input": {
fontWeight: "bold",
color: "blue",
},
}}
/>
</TableRow>
</TableFooter>
</Table>
</TableContainer>
);
};
I would like to know how I can make a sticky column (I would like to have the same effect as stickyHeader). On the other hand, I have tried to center out the pagination component by adding this on the TablePagination component but it is not centering the component:
sx={{
'.MuiTablePagination-root': {
display: 'flex',
justifyContent: 'center',
},
}}
I would appreciate any help on the regard.
sx={{ display: 'flex', justifyContent: 'center' }} works for me
i have a key prop error that i can't resolve. I guess i should pass the key prop but it does not work. Here is my map function
<TableRow key={row.id} sx={{ '&:last-child td, &:last-child th': { border: 0 } }}>
<TableCell component="th" scope="row">
<Person lastname={row.lastname} firstname={row.firstname} />
</TableCell>
{CalculateHours().map((c) => ( DisplayDate(c, {row}) ))}
</TableRow>
And here is my function:
const DisplayDate = (c, {row}) => {
if (ParsingTime(row.startDate) > c && ParsingTime(row.startDate) < c+1) {
return <TableCell key={c} style={cellStyle}><hr style={hrStyle.hr1} width={calculatedWidth(ParsingTime(row.startDate), true)}/></TableCell>
} else if(ParsingTime(row.endDate) > c && ParsingTime(row.endDate) < c+1) {
return <TableCell key={c} style={cellStyle}><hr style={hrStyle.hr2} width={calculatedWidth(ParsingTime(row.endDate), false)} /></TableCell>
} else if(ParsingTime(row.startDate) > c || ParsingTime(row.endDate) < c+1) {
return <TableCell key={c} style={cellStyle}><hr style={hrStyle.hr3} /></TableCell>
} else {
return <TableCell key={c} style={cellStyle}><hr style={hrStyle.hr1} /></TableCell>
}
};
How can i pass the key ? Thanks for answers
The react docs says that the keys should be in sibling objects -> https://reactjs.org/docs/lists-and-keys.html#:~:text=Example%3A%20Incorrect%20Key%20Usage
So for your code, it should be in the DisplayDate component when doing the map.
I am new to react and javascript. May I know how to call other class function from current class?
I only able to use prop to pass the data between class.
For example, I have one toolbar and one user class. When I click the toolbar delete button. I want to call delete function in my user class. How to achieve this?
So this is my toolbar. When I click delete button,I want to trigger deleteUser in my user class.
let EnhancedTableToolbar = props => {
const { numSelected, classes, selectedArray } = props;
return (
<Toolbar
className={classNames(classes.root, {
[classes.highlight]: numSelected > 0,
})}
>
<div className={classes.title}>
{numSelected > 0 ? (
<Typography color="inherit" variant="subtitle1">
{numSelected} selected
</Typography>
) : (
<Typography variant="h6" id="tableTitle">
User List
</Typography>
)}
</div>
<div className={classes.spacer} />
<div className={classes.actions}>
{numSelected > 0 ? (
<Tooltip title="Delete">
<IconButton aria-label="Delete">
<DeleteIcon onClick={() => { if (window.confirm('Are you sure you wish to delete '+numSelected +' item?')) deleteUser() } }>
</DeleteIcon>
</IconButton>
</Tooltip>
) : (
<Tooltip title="Filter list">
<IconButton aria-label="Filter list">
<FilterListIcon />
</IconButton>
</Tooltip>
)}
</div>
</Toolbar>
);
};
EnhancedTableToolbar.propTypes = {
classes: PropTypes.object.isRequired,
numSelected: PropTypes.number.isRequired,
selectedArray: PropTypes.array.isRequired,
};
EnhancedTableToolbar = withStyles(toolbarStyles)(EnhancedTableToolbar);
This is my user class. The toolbar is used in this class. I want the deleteUser() get triggered when the toolbar delete button is clicked.
class User extends React.Component {
constructor(props) {
super(props);
this.state = initialState;
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
reset() {
this.setState(initialState);
}
componentDidMount() {
axios.get("http://localhost:4000/readUsers")
.then(json => {
this.setState({data: json.data});
console.log({json})
}).catch(err => console.log("error: "+err));
}
deleteUser(){
}
displayUsers(){
const { classes } = this.props;
const { data, order, orderBy, selected, rowsPerPage, page } = this.state;
const emptyRows = rowsPerPage - Math.min(rowsPerPage, data.length - page * rowsPerPage);
console.log("selected length: "+selected.length+" selected: "+selected);
return(
<Paper className={classes.root}>
<EnhancedTableToolbar numSelected={selected.length} selectedArray={selected} />
<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
onClick={event => this.handleClick(event, n.id)}
role="checkbox"
aria-checked={isSelected}
tabIndex={-1}
key={n.id}
selected={isSelected}
>
<TableCell padding="checkbox">
<Checkbox checked={isSelected} />
</TableCell>
<TableCell component="th" scope="row" padding="none">
{n.id}
</TableCell>
<TableCell align="right">{n.name}</TableCell>
<TableCell align="right">{n.username}</TableCell>
<TableCell align="right">{n.email}</TableCell>
<TableCell align="right">{n.address}</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>
);
}
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: [] });
};
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 });
};
isSelected = id => this.state.selected.indexOf(id) !== -1;
render() {
const { classes } = this.props;
return (
<GridContainer>
<GridItem xs={12} sm={12} md={12}>
{this.checkCurrentButton()}
<Card>
<CardHeader color="primary">
<h4 className={classes.cardTitleWhite}>User List</h4>
<p className={classes.cardCategoryWhite}>
{}
</p>
</CardHeader>
<CardBody>
{this.displayUser()}
</CardBody>
<CardFooter>
</CardFooter>
</Card>
</GridItem>
</GridContainer>
);
}
}
User.propTypes = {
classes: PropTypes.object.isRequired,
};
You can just pass the function as a prop to the toolbar from your parent class and then access it like any other prop with this.props.deleteUser() like so:
let EnhancedTableToolbar = props => {
const { numSelected, classes, selectedArray } = props;
return (
<Toolbar
className={classNames(classes.root, {
[classes.highlight]: numSelected > 0,
})}
>
<div className={classes.title}>
{numSelected > 0 ? (
<Typography color="inherit" variant="subtitle1">
{numSelected} selected
</Typography>
) : (
<Typography variant="h6" id="tableTitle">
User List
</Typography>
)}
</div>
<div className={classes.spacer} />
<div className={classes.actions}>
{numSelected > 0 ? (
<Tooltip title="Delete">
<IconButton aria-label="Delete">
<DeleteIcon onClick={() => { if (window.confirm('Are you sure you wish to delete '+numSelected +' item?')) this.props.deleteUser() } }>
</DeleteIcon>
</IconButton>
</Tooltip>
) : (
<Tooltip title="Filter list">
<IconButton aria-label="Filter list">
<FilterListIcon />
</IconButton>
</Tooltip>
)}
</div>
</Toolbar>
);
};
EnhancedTableToolbar.propTypes = {
classes: PropTypes.object.isRequired,
numSelected: PropTypes.number.isRequired,
selectedArray: PropTypes.array.isRequired,
};
EnhancedTableToolbar = withStyles(toolbarStyles)(EnhancedTableToolbar);
And your User Class:
class User extends React.Component {
constructor(props) {
super(props);
this.state = initialState;
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
reset() {
this.setState(initialState);
}
componentDidMount() {
axios.get("http://localhost:4000/readUsers")
.then(json => {
this.setState({data: json.data});
console.log({json})
}).catch(err => console.log("error: "+err));
}
deleteUser(){
}
displayUsers(){
const { classes } = this.props;
const { data, order, orderBy, selected, rowsPerPage, page } = this.state;
const emptyRows = rowsPerPage - Math.min(rowsPerPage, data.length - page * rowsPerPage);
console.log("selected length: "+selected.length+" selected: "+selected);
return(
<Paper className={classes.root}>
<EnhancedTableToolbar deleteUser={this.deleteUser} numSelected={selected.length} selectedArray={selected} />
<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
onClick={event => this.handleClick(event, n.id)}
role="checkbox"
aria-checked={isSelected}
tabIndex={-1}
key={n.id}
selected={isSelected}
>
<TableCell padding="checkbox">
<Checkbox checked={isSelected} />
</TableCell>
<TableCell component="th" scope="row" padding="none">
{n.id}
</TableCell>
<TableCell align="right">{n.name}</TableCell>
<TableCell align="right">{n.username}</TableCell>
<TableCell align="right">{n.email}</TableCell>
<TableCell align="right">{n.address}</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>
);
}
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: [] });
};
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 });
};
isSelected = id => this.state.selected.indexOf(id) !== -1;
render() {
const { classes } = this.props;
return (
<GridContainer>
<GridItem xs={12} sm={12} md={12}>
{this.checkCurrentButton()}
<Card>
<CardHeader color="primary">
<h4 className={classes.cardTitleWhite}>User List</h4>
<p className={classes.cardCategoryWhite}>
{}
</p>
</CardHeader>
<CardBody>
{this.displayUser()}
</CardBody>
<CardFooter>
</CardFooter>
</Card>
</GridItem>
</GridContainer>
);
}
}
User.propTypes = {
classes: PropTypes.object.isRequired,
};
You need to pass the deleteUser function from your parent component to the child component. You can do this in the props. You're calling the EnhancedTableToolbar inside of the displayUsers method, so in it do the following:
return(
<Paper className={classes.root}>
<EnhancedTableToolbar
numSelected={selected.length}
selectedArray={selected}
deleteUser={this.deleteUser} // you pass the deleteUser of the parent component to the EnhancedTableToolbar as a prop
/>
Then use it inside of the EnhancedTableToolbar
<IconButton aria-label="Delete">
<DeleteIcon onClick={() => { if (window.confirm('Are you sure you wish to delete '+numSelected +' item?')) this.props.deleteUser() } }>
</DeleteIcon>
Your Toolbar component has to receive a property such as onDeleteUser
This way the parent can give the Toolbar a way for it to “call back” when something such as a click happened.
So Toolbar will do something like onClick={this.props.onDeleteUser}
When someone clicks and that event fires, the function is called and the Parent can delete the user.
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.
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>