1

Using React.Js When trying to delete users from the end of the list the deleted users are replaced by the last item in the list. Only on page refresh do the deleted users actually go away and the user list updates. The back-end is work because I can delete the user but I need to refresh to see a new update.

RemoveUser file :

const Transition = React.forwardRef(function Transition(props, ref) {
    return <Slide direction="up" ref={ref} {...props} />;
});



const combinedStyles = combineStyles(popupStyles, UserPageStyles);

export default function RemoveUser(props) {
    const global = useContext(GlobalContext);
    const [open, setOpen] = React.useState(false);
    let org = {}
    if (global.state.selectedOrg && Object.keys(global.state.selectedOrg).length !== 0) {
        org = global.state.selectedOrg
    } else if (global.state.defaultOrg && Object.keys(global.state.defaultOrg).length !== 0) {
        org = global.state.defaultOrg
    }
    const handleClickOpen = () => {
        setOpen(true);
    };

    const handleClose = () => {
        setOpen(false);
        props.handleClose();
    };
    const handleSubmit = async () => {
        let result = await global.api.removeAccount({
            id: props.account._id,
            role: global.state.user && global.state.user.document.roles[0] || '',
            accountRole: props.type === 'patients' ? 'patient' : props.type === 'providers' ? 'doctor' : props.type === 'moas' ? 'oa' : props.type === 'admins' ? 'healthOrgAdmin' : props.type,
            org: org._id,
            username: props.account.username,
        });
        if (result && result.status === 200) {
            handleClose();
            props.refresh();
        } else {
            alert('Unable to remove account.');
        }
    }

    const classes = combinedStyles();

    return (
        <div>
            <ButtonBase className={props.className} onClick={handleClickOpen}> <Typography className={classes.typography}>Remove</Typography></ButtonBase>

            <Dialog
                open={open}
                TransitionComponent={Transition}
                keepMounted
                onClose={handleClose}
                aria-labelledby="alert-dialog-slide-title"
                aria-describedby="alert-dialog-slide-description"
            >

                <DialogTitle className={classes.dialogTitle} id="alert-dialog-slide-title">Remove Account<IconButton onClick={handleClose} className={classes.dialogClose} children={<ClearIcon />} /> </DialogTitle>
                <DialogContent>
                    <DialogContentText className={classes.contentText}>
                        Are you sure you want to remove {props.account.contact_name}'s account? You will not be able to revert this.
                    </DialogContentText>
                </DialogContent>

                <DialogActions className={classes.dialogAction}>
                    <Button onClick={handleClose} color="primary" className={classes.actionBtn}>
                        Cancel
                    </Button>
                    <Button onClick={handleSubmit} color="primary" className={classes.actionBtn}>
                        Yes
                    </Button>
                </DialogActions>
            </Dialog>
        </div>
    );
}

userTable file

function UsersTable(props) {

    const classes = useStyles();
    const global = React.useContext(GlobalContext)
    const [anchorEl, setAnchorEl] = React.useState(null);
    const [anchorEl2, setAnchorEl2] = React.useState(null);
    const [searchText, setSearchText] = React.useState("")
    const [userTypeName, setUserTypeName] = React.useState(null);
    const [loading, setLoading] = React.useState(true)
    const [rows, setRows] = React.useState([])
    const [doctors, setDoctors] = React.useState([])
    const [userList, setUserList] = React.useState([])
    const handleClick = (event) => {
        setAnchorEl(event.currentTarget);
    };
    const handleClick2 = (event) => {
        setAnchorEl2(event.currentTarget);
    };
    const [page, setPage] = React.useState(1);
    const [maxPage, setMaxPage] = React.useState(1);
    const [openSignUp, setOpenSignUp] = React.useState()
    const rowsPerPage=6
    const handleChangePage = (event, value) => {
        setPage(value);
    };
    const handleClose = () => {
        setAnchorEl(null);
    };
    const handleClose2 = () => {
        setAnchorEl2(null);
    };
    
    //select postion end
    const open = Boolean(anchorEl);
    const id = open ? 'simple-popover' : undefined;
    const open2 = Boolean(anchorEl2);
    const id2 = open2 ? 'simple-popover' : undefined;



    const loadUserData= async () => {
        let currentOrg
        if (!global.state) return
        if (!global.state || !global.state.selectedOrg || global.state.selectedOrg == null || JSON.stringify(global.state.selectedOrg) == "{}") {
            currentOrg = global.state.defaultOrg
        } else {
            currentOrg = global.state.selectedOrg

        }
        
        let response
        if (props.type == "patients") {
            response = await global.api.searchUsersByOrg(currentOrg._id, "patient", "", -1)
        }
        else if (props.type == "providers") {
            response = await global.api.searchUsersByOrg(currentOrg._id, "doctor", "", -1)
        } 
        else if (props.type == "moas") {
            response = await global.api.searchUsersByOrg(currentOrg._id, "oa", "", -1)
        } 
        else if (props.type == "admins") {
            response = await global.api.searchUsersByOrg(currentOrg._id, "healthOrgAdmin", "", -1)
        }    
        setUserList([])
        if (response && (response.status == true || response.status == 200)) {
            if (response.data) {
                let data = response.data.data
                let formatedData = data.map((v, i) => {
                    if (!v.contact_name) {
                        if(v.firstname&&v.lastname)  v.contact_name = v.firstname + " " + v.lastname
                        if(v.name) v.contact_name =v.name
                        if(!v.contact_name) v.contact_name="N/A"
                    }
                    if (!v.email && v.emails) {
                        v.email = v.emails[0].address
                    } else if (!v.email) {
                        v.email = "N/A"
                    }
                    if (!v.phone){
                        v.phone = "N/A"
                        if(v.applicant) v.phone=v.applicant
                   
                    } 

                    return v
                })
                setUserList(formatedData)
                setRows(formatedData)
                let max= Math.ceil(formatedData.length/6)
                setMaxPage(max)

            }
        }
    }


    const refreshUsers = async ()=>{
        let currentOrg
        if (!global.state) return
        if (!global.state || !global.state.selectedOrg || global.state.selectedOrg == null || JSON.stringify(global.state.selectedOrg) == "{}") {
            currentOrg = global.state.defaultOrg
        } else {
            currentOrg = global.state.selectedOrg

        }
        
        let response
        if (props.type == "patients") {
            response = await global.api.searchUsersByOrg(currentOrg._id, "patient", "", 6, page)
        }
        else if (props.type == "providers") {
            response = await global.api.searchUsersByOrg(currentOrg._id, "doctor", "", 6, page)
        } 
        else if (props.type == "moas") {
            response = await global.api.searchUsersByOrg(currentOrg._id, "oa", "", 6, page)
        } 
        else if (props.type == "admins") {
            response = await global.api.searchUsersByOrg(currentOrg._id, "healthOrgAdmin", "", 6, page)
        }    
        let arr=[]
        if (response && (response.status == true || response.status == 200)) {
            if (response.data) {
                let data = response.data.data
                let formatedData = data.map((v, i) => {
                    if (!v.contact_name) {
                        if(v.firstname&&v.lastname)  v.contact_name = v.firstname + " " + v.lastname
                        if(v.name) v.contact_name =v.name
                        if(!v.contact_name) v.contact_name="N/A"
                    }
                    if (!v.email && v.emails) {
                        v.email = v.emails[0].address
                    } else if (!v.email) {
                        v.email = "N/A"
                    }
                    if (!v.phone){
                        v.phone = "N/A"
                        if(v.applicant) v.phone=v.applicant
                   
                    } 
                    arr.push(v)
                    return v
                })
                const tempRow=[...rows]
                for(let i=0; i<formatedData.length; i++){
                    tempRow[((page-1)*6)+i]=formatedData[i]
                }
                setUserList(tempRow)
                setRows(tempRow)

            }
        }
    }
    
    const enableAccount = async (id) => {
        let result = await global.api.updateAccount({ id: id, accountDisabled: false })
        if (result && result.status === 200) {
            loadUserData()
        }
        
    }


    React.useEffect(()=>{
        let result=[]

        result=userList.filter(name => name.contact_name.toLowerCase().includes(props.searchText.toLowerCase()))
       
        setRows(result)
        let max=Math.ceil(result.length/6)
        setMaxPage(max)
        setPage(1)

    }, [props.searchText])


    React.useEffect(() => {
        setLoading(true)
        setRows([])
        if (props.type === "patients" || props.type === "providers" || props.type === "moas" || props.type === "Organizations" || props.type === 'admins') {
            loadUserData()
        }
        setLoading(false)

    
    }, [props.type, global.state.selectedOrg, global.state.defaultOrg]) //removed searchText and page
    
    React.useEffect(() => {
        setLoading(true)
        setRows([])
        if (props.type === "patients" || props.type === "providers" || props.type === "moas" || props.type === "Organizations" || props.type === 'admins') {
            refreshUsers()
        }
        setLoading(false)

    
    }, [page])


    React.useEffect(() => {
        setPage(1)
    }, [props.type])
    
    React.useEffect(() => {
        let currentOrg = {}
        if (!global.state) return
        if (!global.state.selectedOrg || !Object.keys(global.state.selectedOrg).length) {
            currentOrg = global.state.defaultOrg
        } else {
            currentOrg = global.state.selectedOrg
        }
        const fetchDoctorList = async () => {
            try {
                const response = await global.api.getDoctorsFromEmr(currentOrg._id)
                setDoctors(response.data)
                
            } catch (error) {
                console.log(error.response ? error.response.data : error);
            }    
        }
        fetchDoctorList()
        
    }, [global.state.selectedOrg, global.state.defaultOrg])

    return (

        <Box className={classes.container}>
            {loading || !rows ?
                <h1>loading</h1> :


                <Grid container spacing={3}>
                     
                    <Grid item xs={12}>
                        <TableContainer component={Paper}>
                            <Table className={classes.table} aria-label="customized table">
                                <TableHead>
                                    <TableRow>
                                        <StyledTableCell>Name</StyledTableCell>
                                        <StyledTableCell >Phone</StyledTableCell>
                                        <StyledTableCell>Email</StyledTableCell>
                                        <StyledTableCell ></StyledTableCell>

                                    </TableRow>
                                </TableHead>
                                <TableBody>
                                    {rows.length > 0 && rows.slice((page-1) * rowsPerPage, (page-1) * rowsPerPage + rowsPerPage).map((row, i) => (
                                        <StyledTableRow key={i}>
                                            <StyledTableCell component="th" scope="row">
                                                {row.contact_name}
                                            </StyledTableCell>
                                            <StyledTableCell >{!row.accountDisabled ? row.phone : ''}</StyledTableCell>
                                            <StyledTableCell >{!row.accountDisabled ? (row.email ? row.email : row.emails && row.emails !== null && row.emails.length !== 0 ? (row.emails[0].address ? row.emails[0].address : row.emails[0]) : "") : ''}</StyledTableCell>
                                            {!row.accountDisabled ?
                                            <StyledTableCell align="right" >
                                                {row.action ||
                                                    <Box className={classes.actionFieldLayout}>
                                                       
                                                        {global.state.user && ((global.state.user.document.roles[0] === 'healthOrgAdmin' || global.state.user.document.roles[0] === 'admin') || row.roles[0] === "patient") &&
                                                            <MeatBallButton globalState={global.state} type={props.type} _id={row._id} account={row} refresh={refreshUsers} doctors={doctors} />
                                                        }
                                                       
                                                        {global.state.user && global.state.user.document.roles[0] === 'oa' && row.roles[0] === "doctor" &&
                                                            <MeatBallButton globalState={global.state} type={props.type} _id={row._id} account={row} refresh={refreshUsers} doctors={doctors} />
                                                        }
                                                        <ContactButton info={row} globalState={global.state} className={classes.contactButton} />
                                                        {(row.roles[0] === "patient" || row.roles[0] === "doctor") &&
                                                            <CalendarButton type={props.type} id={row._id} className={classes.contactButton} />
                                                        }
                                                        
                                                    </Box>
                                                }
                                            </StyledTableCell>
                                            :
                                            <StyledTableCell align="right">
                                                {global.state.user && (global.state.user.document.roles[0] === 'healthOrgAdmin' || global.state.user.document.roles[0] === 'admin') ?
                                                    <Button color='secondary' onClick={() => enableAccount(row._id)}>Enable Account</Button>
                                                    :
                                                    <Typography color='secondary'>Account Disabled</Typography>
                                                }
                                            </StyledTableCell>
                                            }

                                        </StyledTableRow>
                                    ))}
                                </TableBody>
                            </Table>
                        </TableContainer>
                    </Grid>
                    <Grid container
                        spacing={0}
                        direction="column"
                        alignItems="center"
                        justify="center"
                        item xs={12}>
                        <Grid item xs={6}>
                            <Pagination count={maxPage} page={page} onChange={handleChangePage} />
                        </Grid>
                    </Grid>

                </Grid>}
        </Box >
    );
}

export default withRouter(UsersTable);


Sarah
  • 11
  • 1
  • 4

1 Answers1

0

Issue

I've not dug all the way through your wall of code, but the issue is caused by using the mapped array index as a React key. When you mutate the underlying data structure and remove elements, say like an element at a specific index, then the elements behind it shift and now the previously used index refers to an entirely new object.

{rows.length > 0 && 
  rows.slice(
    (page-1) * rowsPerPage,
    (page-1) * rowsPerPage + rowsPerPage
  ).map((row, i) => (
    <StyledTableRow key={i}> // <-- not intrinsic to row data!!
      ...
    </StyledTableRow>
)}

Solution

You should use React keys that relate to the date being rendered, i.e. the unique key value should be intrinsic to the data, not its position in the overall data. It seems you are rendering rows of user data, so I'll assume your data has some GUID to uniquely identify it. If it doesn't then I highly suggest you preprocess your table data to augment each row object with a GUID that persists for the life of the component being rendered.

{rows.length > 0 && 
  rows.slice(
    (page-1) * rowsPerPage,
    (page-1) * rowsPerPage + rowsPerPage
  ).map((row, i) => (
    <StyledTableRow key={row.someUniqueFieldId}> // <-- unique key
      ...
    </StyledTableRow>
)}
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • Thank you , but no luck – Sarah Jun 18 '21 at 16:09
  • @Sarah Well, using an index as a React key when mutating the data isn't helping you for sure. Think you could try distilling your code down to a [Minimal, Complete, and Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example) that is well formatted and easy to read that could help us help you. If you could create a *running* codesandbox demo that accurately reproduces the issue that would be great, we could inspect and live debug in it. – Drew Reese Jun 18 '21 at 16:38