Reputation: 3
I have created a common table component in which I am calling in the file that are needed under some table there is an field action which have an edit and delete icon whenever I click on edit icon it should trigger a dropdown that will allow to update the status of component. But when I click on edit icon the table component will refresh so the menu is not opening at suitable position. Please help me
TableComponent
import React, { useState } from 'react';
import { Table, TableHead, TableRow, TableCell, makeStyles, TablePagination, TableSortLabel } from '@material-ui/core';
import PropTypes from 'prop-types';
const useStyles = makeStyles(theme => ({
table: {
marginTop: theme.spacing(3),
'& thead th': {
fontWeight: '600',
color:'white',
backgroundColor: '#143174',
},
'& tbody td': {
fontWeight: '300',
},
},
}))
export default function TableComponent(records, headCells,filterFn) {
const classes = useStyles();
const pages = [10, 50, 100]
const [page, setPage] = useState(0)
const [rowsPerPage, setRowsPerPage] = useState(pages[page])
const [order, setOrder] = useState()
const [orderBy, setOrderBy] = useState()
const TblContainer = props => (
<Table className={classes.table}>
{props.children}
</Table>
)
TblContainer.propTypes = {
children:PropTypes.element
}
const TblHead = props => {
const handleSortRequest = cellId => {
const isAsc = orderBy === cellId && order === "asc";
setOrder(isAsc ? 'desc' : 'asc');
setOrderBy(cellId)
}
return (<TableHead>
<TableRow>
{
headCells.map(headCell => (
<TableCell key={headCell.id}
sortDirection={orderBy === headCell.id ? order : false}>
{headCell.disableSorting ? headCell.label :
<TableSortLabel
active={orderBy === headCell.id}
direction={orderBy === headCell.id ? order : 'asc'}
onClick={() => { handleSortRequest(headCell.id) }}>
{headCell.label}
</TableSortLabel>
}
</TableCell>))
}
</TableRow>
</TableHead>)
}
const handleChangePage = (event, newPage) => {
setPage(newPage);
}
const handleChangeRowsPerPage = event => {
setRowsPerPage(parseInt(event.target.value, 10))
setPage(0);
}
const TblPagination = () => (<TablePagination
component="div"
page={page}
rowsPerPageOptions={pages}
rowsPerPage={rowsPerPage}
count={records.length}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
/>)
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]);
}
function getComparator(order, orderBy) {
return order === 'desc'
? (a, b) => descendingComparator(a, b, orderBy)
: (a, b) => -descendingComparator(a, b, orderBy);
}
function descendingComparator(a, b, orderBy) {
if (b[orderBy] < a[orderBy]) {
return -1;
}
if (b[orderBy] > a[orderBy]) {
return 1;
}
return 0;
}
const recordsAfterPagingAndSorting = () => {
return stableSort(filterFn.fn(records), getComparator(order, orderBy))
.slice(page * rowsPerPage, (page + 1) * rowsPerPage)
}
return {
TblContainer,
TblHead,
TblPagination,
recordsAfterPagingAndSorting
}
}
ParentCoponent
import React,{ useState, useEffect, useCallback } from 'react';
import { Button, Typography, Grid, TextField, MenuItem, Menu, IconButton, Link, TableBody, TableRow, TableCell, Paper, makeStyles } from '@material-ui/core';
import Modals from './Components/Modals';
import DropDownComponent from './Components/DropDownComponent';
import PropTypes from 'prop-types';
import { fetchImage,postImage, removeImage, updateImageStatus } from './app/ActionCreator';
import { connect } from 'react-redux';
import EditStatusComponent from './Components/EditStatusComponent';
import { Edit, Delete } from '@material-ui/icons';
import TableComponent from './Components/TableComponent';
const styles = makeStyles(theme => ({
root:{
marginTop:'60px',
},
typo:{
flexGrow:1,
textAlign:'center',
marginTop:'30px',
color:'black'
},
headingpaper:{
width:'100%',
height:40,
marginTop:'30px',
background:'white',
borderColor:'white',
},
buttonColor:{
color:'white',
background:'#143174'
},
formControl: {
margin:'8px',
minWidth: 120,
},
selectEmpty: {
marginTop: '16px',
},
formRoot:{
margin:'8px',
width: '25ch',
},
modalButton:{
color:'white',
background:'#143174',
marginRight:'10px'
}
}))
const headCells = [
{id:1,label:'SlNo'},
{id:2,label:'Virtual Machine Image '},
{id:3,label:'createdAt'},
{id:4,label:'createdBy'},
{id:5,label:'IpAddress'},
{id:6,label:'Status'},
{id:7,label:'Action',disableSorting:true}
]
function AdminImages(props){
const [imageModal,setImageModal] = useState(false)
const [name,setName] = useState('')
const [cpu,setCpu] = useState('')
const [ram,setRam] = useState('')
const [disk,setDisk] = useState('')
const [imageid,setImageid] = useState('')
const [anchorEl,setAnchorEl] = useState(null)
const [filterFn, setFilterFn] = useState({ fn: items => { return items; } })
const handlecpuChange = (event) => {
setCpu(event.target.value)
}
const handleramChange = (event) => {
setRam(event.target.value)
}
const handlediskChange = (event) => {
setDisk(event.target.value)
}
useEffect(() => {
props.fetchImageData()
},[])
const handleMenu = useCallback((event,id) => {
setAnchorEl(event.currentTarget)
setImageid(id)
},[])
const handleClose = (ev) => {
setAnchorEl(null)
if(ev.target.innerText !== ''){
const data = {
"status":ev.target.innerText
}
props.updateImageStatusData(imageid,data)
}
}
const newInstance = () => {
if(name !== '' && cpu !== '' && ram !== '' && disk !== ''){
props.postImageData(name,cpu,ram,disk)
}
else {
alert("Please Enter All Field")
}
setImageModal(false)
}
const labstatus = (status) => {
if(status === 'Resuming' || status === 'Running'){
return(
<EditStatusComponent
status={status}
statuscolor='#32CD32'
showDropDown={false}/>
)
}
else if(status === 'Suspending' || status === 'suspending' || status === 'error'){
return(
<EditStatusComponent
status={status}
statuscolor='red'
showDropDown={false}/>
)
}
else{
return(
<EditStatusComponent
status={status}
statuscolor='#143174'
showDropDown={false}/>
)
}
}
const handleSearch = e => {
let target = e.target;
setFilterFn({
fn: items => {
if (target.value == "")
return items;
else
return items.filter(x => x.instance_name.toLowerCase().includes(target.value))
}
})
}
const classes = styles()
const { TblContainer, TblHead, TblPagination, recordsAfterPagingAndSorting } = TableComponent(props.Images.images,headCells,filterFn)
return(
<div className={classes.root}>
<Grid container direction="row" justify="flex-end" alignItems="flex-start">
<Button variant="contained" className={classes.buttonColor} onClick={() => setImageModal(true)}>
Create Images
</Button>
</Grid>
<Typography variant="h2" noWrap className={classes.typo}>
Virtual Machine Image
</Typography>
<Paper>
<TblContainer>
<TblHead/>
<TableBody>
{
recordsAfterPagingAndSorting().map((v,i) => (
<TableRow key={v.id}>
<TableCell>{v.id}</TableCell>
<TableCell>{v.instance_name}</TableCell>
<TableCell>{v.created_at}</TableCell>
<TableCell>{v.created_by.first_name + v.created_by.last_name}</TableCell>
<TableCell><Link target={"_blank"} onClick={() => window.open(`http://${v.ip_address}/project`,'_blank')}>{v.ip_address}</Link></TableCell>
<TableCell>{labstatus(v.status)}</TableCell>
<TableCell>
<div style={{display:'flex'}}>
<IconButton
aria-controls="simple-menu"
aria-haspopup="true"
onClick={(e)=> handleMenu(e,v.id)}>
<Edit/>
</IconButton>
<IconButton onClick={() => props.removeImageData(v.id)}>
<Delete/>
</IconButton>
</div>
</TableCell>
</TableRow>
))
}
</TableBody>
</TblContainer>
<TblPagination/>
</Paper>
<Menu
id="simple-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}>
<MenuItem value="Resume" onClick={handleClose}>Resume</MenuItem>
<MenuItem value="Suspend" onClick={handleClose}>Suspend</MenuItem>
<MenuItem value="Freeze" onClick={handleClose}>Freeze</MenuItem>
</Menu>
<div>
<Modals
show={imageModal}
title="Create Instance"
handleClose={() => setImageModal(true)}
showSubmit={true}
onPress={newInstance}
size="xs">
<TextField
id="standard-basic"
label="Name"
style={{width:'50ch',margin:10}}
value={name}
onChange={(e) => setName(e.target.value)}
InputLabelProps={{
shrink: true,
}}/>
<DropDownComponent
dropdownid="standard-select-cpu"
selectedValue={cpu}
handleChange={handlecpuChange}
dropdownLabel="CPU">
<MenuItem value="8">8</MenuItem>
<MenuItem value="10">10</MenuItem>
<MenuItem value="12">12</MenuItem>
<MenuItem value="14">14</MenuItem>
<MenuItem value="16">16</MenuItem>
<MenuItem value="18">18</MenuItem>
<MenuItem value="20">20</MenuItem>
<MenuItem value="22">22</MenuItem>
<MenuItem value="24">24</MenuItem>
<MenuItem value="26">26</MenuItem>
<MenuItem value="28">28</MenuItem>
<MenuItem value="30">30</MenuItem>
<MenuItem value="32">32</MenuItem>
</DropDownComponent>
<DropDownComponent
dropdownid="standard-select-ram"
selectedValue={ram}
handleChange={handleramChange}
dropdownLabel="RAM">
<MenuItem value="16">16</MenuItem>
<MenuItem value="20">20</MenuItem>
<MenuItem value="24">24</MenuItem>
<MenuItem value="28">28</MenuItem>
<MenuItem value="32">32</MenuItem>
<MenuItem value="36">36</MenuItem>
<MenuItem value="40">40</MenuItem>
<MenuItem value="44">44</MenuItem>
<MenuItem value="48">48</MenuItem>
<MenuItem value="52">52</MenuItem>
<MenuItem value="56">56</MenuItem>
<MenuItem value="60">60</MenuItem>
<MenuItem value="64">64</MenuItem>
</DropDownComponent>
<DropDownComponent
dropdownid="standard-select-disk"
selectedValue={disk}
handleChange={handlediskChange}
dropdownLabel="Disk">
<MenuItem value="50">50</MenuItem>
<MenuItem value="100">100</MenuItem>
<MenuItem value="150">150</MenuItem>
<MenuItem value="200">200</MenuItem>
<MenuItem value="250">250</MenuItem>
<MenuItem value="300">300</MenuItem>
<MenuItem value="350">350</MenuItem>
<MenuItem value="400">400</MenuItem>
<MenuItem value="450">450</MenuItem>
<MenuItem value="500">500</MenuItem>
</DropDownComponent>
</Modals>
</div>
</div>
)
}
const mapStateToProps = state => {
return {
Images:state.Images,
}
}
const mapDispatchToProps = dispatch => ({
fetchImageData:() => {
dispatch(fetchImage())
},
postImageData:(name,cpu,ram,storage) => {
dispatch(postImage(name,cpu,ram,storage))
},
removeImageData:(id) => {
dispatch(removeImage(id))
},
updateImageStatusData:(id,data) => {
dispatch(updateImageStatus(id,data))
}
})
AdminImages.propTypes = {
classes:PropTypes.object.isRequired,
Images:PropTypes.object,
fetchImageData:PropTypes.func,
postImageData:PropTypes.func,
removeImageData:PropTypes.func,
updateImageStatusData:PropTypes.func,
}
export default connect(mapStateToProps,mapDispatchToProps)(AdminImages)
Upvotes: 0
Views: 103
Reputation: 777
You can prevent unnecessary re-renders of a child component in 2 ways
React.PureComponent
or implement shouldComponentUpdate
lifecycle method by yourself.class MyComponent extends React.PureComponent {
// your component logic
}
class MyComponent extends Component {
shouldComponentUpdate(nextProps, nextState) {
//compare nextProps and this.props
//compare nextState and this.state
//return true for re-render, otherwise false
}
// your component logic
}
memo
function that will check only for prop changes or you can pass it a function in the second argument that will do the current props and next props comparison and return true/false if you want do manual comparison just like shouldComponentUpdate
const MyComponent = React.memo(function(props) {
/* render using props */
});
function MyComponent(props) {
/* render using props */
}
function areEqual(prevProps, nextProps) {
/*
return true if passing nextProps to render would return
the same result as passing prevProps to render,
otherwise return false
*/
}
export default React.memo(MyComponent, areEqual);
Here are some useful links from the docs:
Upvotes: 1