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.
Related
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
I have the following react components:
class Statuses extends React.Component {
constructor(props) {
super();
this.groups = props.groups;
this.state = {
statuses: [],
editDialog: null,
deleteDialog: null,
editTransitionsDialog: null,
transitions: null,
editTransitionGroupsDialog: null,
transition: null,
groups: null,
alert: null,
};
}
componentDidMount() {
this.fetchStatuses();
}
handleCloseAlert = () => {
let alertType = this.props.alert.type;
this.props.cleanCatalogsMessage();
if (alertType == ALERT_SUCCESS) {
this.setState({editDialog: null});
}
}
TransitionRight = (props) => {
return <Slide {...props} direction="right" />;
}
fetchStatuses = () => {
this.props.fetchStatuses(this.props.currentForm.id)
}
handleEditClick = (id) => {
if (id) {
this.setState({editDialog: this.props.statuses.find(status => status.id === id)});
} else {
this.setState({editDialog: {id: null, name: null, description: null, color: '#808080'}})
}
}
handleCloseEditDialog = (proceed) => {
if (proceed) {
this.saveStatus();
} else {
this.setState({editDialog: null});
};
}
handleEditStatusChange = (e) => {
let newEditDialog = Object.assign({}, this.state.editDialog)
switch (e.target.name) {
case 'status_name': {
newEditDialog.name = e.target.value;
break;
}
case 'status_description': {
newEditDialog.description = e.target.value;
break;
}
case 'status_slug': {
newEditDialog.slug = e.target.value;
break;
}
case 'status_color': {
newEditDialog.color = e.target.value;
break;
}
default: {
alert("Error on handleEditTransitionChange");
}
}
this.setState({editDialog: newEditDialog});
}
saveStatus = () => {
if (this.state.editDialog.id) {
this.updateStatus();
} else {
this.createStatus();
}
}
updateStatus = () => {
this.props.updateStatus(this.state.editDialog);
}
createStatus = () => {
// const requestString = backendServer + 'api/general/statuses/';
const form = {
automat: this.props.currentForm.id,
name: this.state.editDialog.name,
description: this.state.editDialog.description,
slug: this.state.editDialog.slug,
color: this.state.editDialog.color,
}
this.props.createStatus(form);
}
handleDeleteClick = (id) => {
this.setState({deleteDialog: this.state.statuses.find(status => status.id === id)});
}
handleCloseDeleteDialog = (proceed) => {
if (proceed) {
this.deleteStatus()
} else {
this.setState({deleteDialog: null});
}
}
deleteStatus = () => {
const requestString = backendServer + 'api/general/statuses/' + this.state.deleteDialog.id + '/';
axios.delete(requestString, getAuthHeader())
.then((Response) => {
let newStatusesList = this.state.statuses.slice();
let i = newStatusesList.findIndex((status) => status.id === this.state.deleteDialog.id);
newStatusesList.splice(i, 1);
newStatusesList.forEach((status) => {
let j = status.transitions.findIndex((transition) => transition.id === this.state.deleteDialog.id)
if (j > -1) {
status.transitions.splice(j, 1);
}
})
this.setState({statuses: newStatusesList, deleteDialog: null, alert: "¡El estado se ha eliminado con Éxito!"});
})
.catch((Error) => {
const message = getErrorMessage(Error).message.message;
alert(message)
})
}
handleEditTransitionsClick = (id) => {
let statusToEdit = this.props.statuses.find(status => status.id === id)
this.setState({
editTransitionsDialog: statusToEdit,
transitions: statusToEdit.transitions.slice()
});
}
handleEditTransitionsChange = (e) => {
let newTransitionsList = null;
if (e.target.checked) {
newTransitionsList = this.state.transitions.slice();
let statusToAdd = this.props.statuses.find((status) => status.name === e.target.name);
let transitionToAdd = {id: statusToAdd.id, name: statusToAdd.name};
newTransitionsList.push(transitionToAdd);
} else {
newTransitionsList = this.props.transitions.slice();
let i = newTransitionsList.findIndex((transition) => transition.name === e.target.name);
if (i > -1) {
newTransitionsList.splice(i, 1);
}
}
this.setState({transitions: newTransitionsList});
}
handleCloseEditTransitionsDialog = (proceed) => {
if (proceed) {
this.updateTransitions();
} else {
this.setState({editTransitionsDialog: null, transitions: null});
}
}
updateTransitions = () => {
let transitionsToAdd = this.state.transitions.filter(transition => !this.state.editTransitionsDialog.transitions.includes(transition));
let transitionsToDelete = this.state.editTransitionsDialog.transitions.filter(transition => !this.state.transitions.includes(transition));
const requestString = backendServer + 'api/general/set_transitions/' + this.state.editTransitionsDialog.id + '/';
axios.post(requestString, {add: transitionsToAdd, delete: transitionsToDelete}, getAuthHeader())
.then((Response) => {
let newEditTransitionsDialog = Object.assign({}, this.state.editTransitionsDialog);
transitionsToAdd.forEach((transition) => {
newEditTransitionsDialog.transitions.push({id: transition.id, name: transition.name, groups: []});
})
transitionsToDelete.forEach((transition) => {
let i = newEditTransitionsDialog.transitions.findIndex((transitionToEval) => transitionToEval.id === transition.id);
newEditTransitionsDialog.transitions.splice(i, 1);
})
let newStatusesList = this.state.statuses.slice();
let j = newStatusesList.findIndex((status) => status.id === this.state.editTransitionsDialog.id);
newStatusesList[j] = newEditTransitionsDialog;
this.setState({statuses: newStatusesList, editTransitionsDialog: null, transitions: null, alert: "¡Las transiciones se modificaron con éxito!"});
})
.catch((Error) => {
const message = getErrorMessage(Error).message.message;
alert(message)
})
}
handleEditTransitionGroupsClick = (id, transition) => {
let newEditTransitionGroupsDialog = Object.assign({}, this.props.statuses.find((status) => status.id === id));
let newTransition = Object.assign({}, newEditTransitionGroupsDialog.transitions.find((transitionToEval) => transitionToEval.id === transition));
this.setState({editTransitionGroupsDialog: newEditTransitionGroupsDialog, transition: newTransition, groups: newTransition.groups.slice()});
}
handleEditTransitionGroupsChange = (e) => {
let newGroupsList = this.state.groups.slice();
if (e.target.checked) {
let newGroup = this.groups.find((group) => group.name === e.target.name);
newGroupsList.push({id: newGroup.id, name: newGroup.name});
} else {
let i = this.groups.findIndex((group) => group.name === e.target.name);
newGroupsList.splice(i, 1);
}
this.setState({groups: newGroupsList});
}
handleCloseEditTransitionGroupsDialog = (proceed) => {
if (proceed) {
this.updateGroups();
} else {
this.setState({editTransitionGroupsDialog: null, transition: null, groups: null});
}
}
updateGroups = () => {
let groupsToAdd = this.state.groups.filter(group => !this.state.transition.groups.includes(group));
let groupsToDelete =this.state.transition.groups.filter(group => !this.state.groups.includes(group));
const requestString = backendServer + 'api/general/set_groups/' + this.state.transition.id + '/';
axios.post(requestString, {automat: this.props.currentForm.id, init_status: this.state.editTransitionGroupsDialog.id, final_status: this.state.transition.id, add: groupsToAdd, delete: groupsToDelete}, getAuthHeader())
.then((Response) => {
let newTransition = Object.assign({}, this.state.transition);
newTransition.groups = this.state.groups.slice()
let newStatus = Object.assign({}, this.state.editTransitionGroupsDialog);
let i = newStatus.transitions.findIndex(transition => transition.id === newTransition.id);
newStatus.transitions[i] = newTransition;
let newStatusesList = this.state.statuses.slice();
i = newStatusesList.findIndex(status => status.id === newStatus.id);
newStatusesList[i] = newStatus;
this.setState({
statuses: newStatusesList,
editTransitionGroupsDialog: null,
transition: null,
groups: null,
alert: "¡Los Grupos se modificaron con éxito!"
});
})
.catch((Error) => {
const message = getErrorMessage(Error).message.message;
alert(message);
})
}
StatusSnackbars = () => {
const classes = useStylesStatusSnackBars();
const [open, setOpen] = React.useState(false);
const handleClick = () => {
setOpen(true);
};
const handleClose = (event, reason) => {
if (reason === 'clickaway') {
return;
}
setOpen(false);
};
return (
<div className={classes.root}>
<Button variant="outlined" onClick={handleClick}>
Open success snackbar
</Button>
<Snackbar open={open} autoHideDuration={6000} onClose={handleClose}>
<Alert onClose={handleClose} severity="success">
This is a success message!
</Alert>
</Snackbar>
<Alert severity="error">This is an error message!</Alert>
<Alert severity="warning">This is a warning message!</Alert>
<Alert severity="info">This is an information message!</Alert>
<Alert severity="success">This is a success message!</Alert>
</div>
);
}
StatusesTable = () => {
return (
<div width="100%">
{this.props.statuses && this.props.statuses.map((status) => {
return (<this.StatusEntry status={status} />)
})}
</div>
)
}
StatusEntry = (status) => {
const classes = useStylesStatusEntry();
return (
<Accordion>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1bh-content"
id="panel1bh-header"
>
<div style={{paddingRight: "5px"}}>
<Chip label={"[" + status.status.id + "]" + status.status.name} size="large" style={{color: "white", backgroundColor: status.status.color}}/>
</div>
<Typography className={classes.secondaryHeading}>{status.status.description}</Typography>
</AccordionSummary>
<AccordionDetails>
<div>
<div style={{display: "flex", alignContent: "center", paddingBottom: "10px"}}>
<div style={{paddingRight: "5px"}}><Button color="primary" variant="contained" size="small" onClick={() => this.handleEditClick(status.status.id)}>Editar</Button></div>
<div style={{paddingRight: "5px"}}><Button color="primary" variant="contained" size="small" onClick={() => this.handleEditTransitionsClick(status.status.id)}>Transiciones</Button></div>
<div style={{paddingRight: "5px"}}><Button color="secondary" variant="contained" size="small" onClick={() => this.handleDeleteClick(status.status.id)}>Eliminar</Button></div>
</div>
<div>
{status.status.transitions.map((transition) => (
<div>
<IconButton aria-label="groups" size="small" color="primary" onClick={() => this.handleEditTransitionGroupsClick(status.status.id, transition.id)}>
<EditIcon fontSize="inherit" />
</IconButton>
<Chip label={transition.name} size="small" style={{color: transition.color}} variant="outlined"/>
{transition.groups.map((group => (
<Chip label={group.name} size="small" variant="outlined" />
)))}
<div fullwidth style={{paddingBottom: "5px", paddingTop: "5px"}}><Divider /></div>
</div>
))}
</div>
</div>
</AccordionDetails>
</Accordion>
)
}
render() {
const getFinalStatusChip = () => {
let finalStatus = this.props.statuses.find((status) => status.id === this.state.transition.id)
return (
<Chip label={finalStatus.name} size="large" style={{color: "white", backgroundColor: finalStatus.color}} />
)
}
return (
<div>
<div style={{display: 'flex', paddingBottom: '5px'}}>
<div className="col-10" style={{paddingTop: '5px'}}>
<h1>Estados: {this.props.currentForm.name}</h1>
</div>
<div className="col-2">
<Fab size="small" color="primary" aria-label="add" onClick={() => this.handleEditClick(null)}>
<AddIcon />
</Fab>
</div>
</div>
<this.StatusesTable/>
{this.state.editDialog && !this.props.showMessage &&
<Dialog open={this.state.editDialog !== null} aria-labelledby="edit-dialog-title">
{this.state.editDialog.id?
<DialogTitle id="edit-dialog-title">Editar Estado: [{this.state.editDialog.id}]{this.state.editDialog.name}</DialogTitle>:
<DialogTitle id="edit-dialog-title">Crear Estado: [ ] Nuevo Estado</DialogTitle>
}
<DialogContent>
<TextField
autoFocus
margin="dense"
id="status-name"
name="status_name"
label="Nombre"
fullWidth
value={this.state.editDialog.name}
onChange={this.handleEditStatusChange}
/>
<TextField
margin="dense"
id="status-description"
name="status_description"
label="Descripción"
fullWidth
multiline={true}
rows={4}
value={this.state.editDialog.description}
onChange={this.handleEditStatusChange}
/>
<TextField
margin="dense"
id="status_slug"
name="status_slug"
label="Slug"
fullWidth
multiline={false}
value={this.state.editDialog.slug}
onChange={this.handleEditStatusChange}
/>
<div style={{paddingTop: "10px"}}>
<p style={{color: "gray"}}>Color</p>
<CompactPicker
color={this.state.editDialog.color}
onChangeComplete={(color) => this.handleEditStatusChange({target: {name: "status_color", value: color.hex}})
}
/>
</div>
</DialogContent>
<DialogActions>
<Button onClick={() => this.handleCloseEditDialog(false)} color="primary">
Cancelar
</Button>
<Button onClick={() => this.handleCloseEditDialog(true)} color="primary">
Aceptar
</Button>
</DialogActions>
</Dialog>
}
{this.state.deleteDialog &&
<Dialog open={this.state.deleteDialog !== null} aria-labelledby="delete-dialog-title">
<DialogTitle id="delete-dialog-title">Eliminar Estado: [{this.state.deleteDialog.id}]{this.state.deleteDialog.name}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
<div style={{color: 'black'}}>
<span>¿Está seguro de que desea eliminar este Estado:?</span>
<div>
<Chip label={this.state.deleteDialog.name} size="large" style={{color: "white", backgroundColor: this.state.deleteDialog.color}} />
</div>
</div>
</DialogContentText>
</DialogContent>
<DialogActions>
<Button color="primary" onClick={() => this.handleCloseDeleteDialog(true)}>
Aceptar
</Button>
<Button color="primary" autoFocus onClick={() => this.handleCloseDeleteDialog(false)}>
Cancelar
</Button>
</DialogActions>
</Dialog>
}
{this.state.editTransitionsDialog &&
<Dialog aria-labelledby="edit-transitions-dialog-title" open={this.state.editTransitionsDialog !== null}>
<DialogTitle id="edit-transitions-dialog-title">Editar Transiciones</DialogTitle>
<DialogContent dividers>
<div>
<Chip label={this.state.editTransitionsDialog.name} size="large" style={{color: "white", backgroundColor: this.state.editTransitionsDialog.color}} />
</div>
<div>
<FormGroup row>
{this.props.statuses.map((status) => {
return (
<FormControlLabel
control={
<Checkbox
checked={this.state.transitions.find((transition) => transition.id === status.id)}
name={status.name}
color="primary"
onChange={this.handleEditTransitionsChange}
/>
}
label={status.name}
/>
)
})}
</FormGroup>
</div>
</DialogContent>
<DialogActions>
<Button color="primary" onClick={() => this.handleCloseEditTransitionsDialog(true)}>
Aceptar
</Button>
<Button color="primary" autoFocus onClick={() => this.handleCloseEditTransitionsDialog(false)}>
Cancelar
</Button>
</DialogActions>
</Dialog>
}
{this.state.editTransitionGroupsDialog &&
<Dialog aria-labelledby="edit-transition-groups-dialog-title" open={this.state.editTransitionGroupsDialog !== null}>
<DialogTitle id="edit-transition-groups-dialog-title">Editar Grupos</DialogTitle>
<DialogContent dividers>
<div>
<Chip label={this.state.editTransitionGroupsDialog.name} size="large" style={{color: "white", backgroundColor: this.state.editTransitionGroupsDialog.color}} />
<ArrowRightIcon />
{getFinalStatusChip()}
</div>
<div>
<FormGroup row>
{this.groups.map((group) => {
return (
<FormControlLabel
control={
<Checkbox
name={group.name}
color="primary"
checked={this.state.groups.find((groupToEval) => groupToEval.id === group.id)}
onChange={this.handleEditTransitionGroupsChange}
/>
}
label={group.name}
/>
)
})}
</FormGroup>
</div>
</DialogContent>
<DialogActions>
<Button color="primary" onClick={() => this.handleCloseEditTransitionGroupsDialog(true)}>
Aceptar
</Button>
<Button color="primary" autoFocus onClick={() => this.handleCloseEditTransitionGroupsDialog(false)}>
Cancelar
</Button>
</DialogActions>
</Dialog>
}
{this.props.showMessage &&
<AttrAlert
displayCase="alert_dialog"
type={this.props.alert.type}
message={this.props.alert.message}
handleCloseAlert={this.handleCloseAlert}
/>}
</div>
)
}
}
const mapStateToProps = ({auth, catalogs}) => {
const {groups} = auth;
const {statuses, alert, showMessage} = catalogs;
return {groups, statuses, alert, showMessage};
}
const mapDispatchToProps = {
fetchStatuses,
createStatus,
updateStatus,
cleanCatalogsMessage
}
export default connect(mapStateToProps, mapDispatchToProps)(Statuses);
The error raises in component StatusesTable. I have debugged and found that this.StatusEntry is never executed because I set a breakpoint inside it, and it is never reached, even when a breackpoint in the line where it is called is always reached.
So StatusEntry is never executed, but the error raises at the end of the map loop. This is the error I get in the inspector console:
Error: Minified React error #130; visit https://reactjs.org/docs/error-decoder.html?invariant=130&args[]=undefined&args[]= for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
And this is the full message of the error:
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.
This error only raises when running the application from minified code in a server with node version 10.19.0, but it runs perfectly when running locally from regular JS code and node version 14.15.3.
I'm trying to recover the selected item on array of elements, I have an API service which returns a list of 5 jobs and a page on React to display the applicants. The rendered table from the list has a menu button for each row, when I click on the menu button it fires the handleMenuOpen event and returns the index of the selected job, but when the See applicants menuItem is clicked the handleSeeApplicantsClick event is fired and the returned value is always 4 (the index of the last job) can you please help me with this?
Thanks in advance
import { useState, useEffect } from 'react';
import ApiService from '../../services/ApiService';
export default function Evaluation(props) {
const [jobs, setJobs] = useState([]);
const [selected, setSelected] = useState(null);
const [anchorEl, setAnchorEl] = useState(null);
const columns = [
{label: 'Description', minWidth: 220, width: 200,},
{label: '', minWidth: 20, width: 20},
];
const loadJobs = async() => {
const result = await ApiService.get('/u/jobs');
setJobs(result.data || []);
};
const loadSelectedJob = async(jobId) => {
const result = await ApiService.get('/u/job/'+jobId);
setSelected(result);
};
useEffect( async() => {
if(!jobs?.length) loadJobs();
}, []);
const handleSeeApplicantsClick = (i) => {
console.log(i);
setAnchorEl(null);
};
const handleMenuOpen = (e, i) => {
console.log(i);
setAnchorEl(e?.currentTarget);
}
return (<div>
<TableContainer component={Paper}>
<Table stickyHeader>
<TableHead>
<TableRow>
{ columns.map((x, i) => <TableCell key={i}>{ x.label }</TableCell>) }
</TableRow>
</TableHead>
<TableBody>
{jobs.map( (x, j) => <TableRow key={x.id} >
<TableCell>
<Typography variant="body2">{ x.description }</Typography>
</TableCell>
<TableCell>
<IconButton onClick={ (e) => handleMenuOpen(e, i) }>
<MenuIcon />
</IconButton>
<Menu anchorEl={anchorEl} open={ Boolean(anchorEl) } onClose={() => handleMenuOpen(null)}>
<MenuItem onClick={ (ex) => { handleSeeApplicantsClick(i) }}>See applicants</MenuItem>
</Menu>
</TableCell>
</TableRow> )}
</TableBody>
</Table>
</TableContainer>
</div>);
}
Edit:
As suggested I've changed the code like this
<IconButton onClick={ (e) => handleMenuOpen(e, j) }>
<MenuIcon />
</IconButton>
But it does not fix the error, the last index is always returned.
Change handleMenuOpen(e, i) to handleMenuOpen(e, j)
i is the final value of your previous columns.map(x, i) whereas you are rendering using jobs.map( (x, j))
change <IconButton onClick={ (e) => handleMenuOpen(e, i) }> to <IconButton onClick={ (e) => handleMenuOpen(e, j) }>
change the function param <IconButton onClick={ (e) => handleMenuOpen(e, i) }>
to <IconButton onClick={ (e) => handleMenuOpen(e, j) }> because in map index value is setting into j, you need to access from j, in your example...
<TableBody>
{jobs.map( (x, j) => <TableRow key={x.id} >
<TableCell>
<Typography variant="body2">{ x.description }</Typography>
</TableCell>
<TableCell>
<IconButton onClick={ (e) => handleMenuOpen(e, i) }>
<MenuIcon />
</IconButton>
<Menu anchorEl={anchorEl} open={ Boolean(anchorEl) } onClose={() => handleMenuOpen(null)}>
<MenuItem onClick={ (ex) => { handleSeeApplicantsClick(i) }}>See applicants</MenuItem>
</Menu>
</TableCell>
</TableRow> )}
</TableBody>
or for best approch you can handle this through by your unique id {x.id} if its unique
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>