Reputation: 21
I am using redux for state management. I am using a component that includes a table and a form to update the entries on that table. I am calling the useSelector at the top of the component. I have a sorting function that the data from useSelector is passed on to, the function sorts it and returns a function, which I then map onto the table. However, everytime I add or edit an entry on the form, the sorting function fails. I noticed it's because the component doesn't re-render when the data in dispatch updates. How can I fix this? *** The Component ***
function Users() {
const classes = useStyles();
const dispatch = useDispatch();
const [ openPopup, setOpenPopup ] = useState(false);
const [ loading ] = useFetchHook(fetchUsers());
const { users } = useSelector((state) => state.user);
const [ filterFunc, setFilterFunc ] = useState({
func: (items) => {
return items;
}
});
//Table hook where "users" data is passed and returns "recordsAfterSorting"
const { TableContainer, TableHeader, TablePagination, recordsAfterSorting } = useTable(
users
);
return (
<TableContainer>
<TableHeader />
<TableBody>
{recordsAfterSorting().map((item) => (
<TableRow key={item.id}>
<TableCell>{item.id}</TableCell>
<TableCell>
{item.firstname} {item.lastname}
</TableCell>
<TableCell>{item.is_admin ? 'Yes' : 'No'}</TableCell>
<TableCell>
{/* <ActionButton color='primary' onClick={() => openInPopup(item)}>
<EditOutlined fontSize='small' />
</ActionButton> */}
<ActionButton
color='secondary'
onClick={() =>
setConfirmDialog({
isOpen: true,
title: `Are you sure you want to delete ${item.firstname}'s account?`,
subtitle: "You can't undo this operation",
onConfirm: () => {
handleDelete(item.id);
}
})}>
<Close fontSize='small' />
</ActionButton>
</TableCell>
</TableRow>
))}
</TableBody>
</TableContainer>
<Popup openPopup={openPopup} setOpenPopup={setOpenPopup} title='Add New User'>
<Registration recordForEdit={recordForEdit} addOrEdit={addOrEdit} />
</Popup>
<ConfirmDialog confirmDialog={confirmDialog} setConfirmDialog={setConfirmDialog} />
)
}
*** TableHook ***
export default function useTable(records, headCells, filterFunc) {
const classes = useStyles();
const pages = [ 10, 25, 50 ];
const [ page, setPage ] = useState(0);
// const [ rowsPerPage, setRowsPerPage ] = useState(pages[page]);
const [ order, setOrder ] = useState();
const [ orderBy, setOrderBy ] = useState();
const handleChangePage = (event, newPage) => setPage(newPage);
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0);
};
const descendingComparator = (a, b, orderBy) => {
if (b[orderBy] < a[orderBy]) return -1;
if (b[orderBy] > a[orderBy]) return 1;
return 0;
};
const getComparator = (order, orderBy) => {
return order === 'desc'
? (a, b) => descendingComparator(a, b, orderBy)
: (a, b) => -descendingComparator(a, b, orderBy);
};
const stableSort = (arr, comparator) => {
const stabilizedThis = arr.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 recordsAfterSorting = () =>
stableSort(filterFunc.func(records), getComparator(order, orderBy)).slice(
page * rowsPerPage,
(page + 1) * rowsPerPage
);
const handleSortRequest = (cellId) => {
const isAscending = orderBy === cellId && order === 'asc';
setOrder(isAscending ? 'desc' : 'asc');
setOrderBy(cellId);
};
const TableContainer = ({ children, size=null }) => (
<Table size={size} className={classes.table}>{children}</Table>
);
const TableHeader = () => (
<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 TablePagination = () => (
<MuiTablePagination
component='div'
page={page}
rowsPerPageOptions={pages}
rowsPerPage={rowsPerPage}
count={records.length}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
/>
);
return { TableContainer, TableHeader, TablePagination, recordsAfterSorting };
}
*** Chrome Error ***
TypeError: Cannot read property 'map' of undefined
stableSort
src/hooks/useTable.js:62
59 | };
60 |
61 | const stableSort = (arr, comparator) => {
> 62 | const stabilizedThis = arr.map((el, index) => [ el, index ]);
| ^ 63 | stabilizedThis.sort((a, b) => {
64 | const order = comparator(a[0], b[0]);
65 | if (order !== 0) return order;
Upvotes: 1
Views: 127
Reputation: 451
Instead of passing records
to the useTable()
pass it direrectly to the recordsAfterSorting()
fn like this:
const recordsAfterSorting = (records) =>
stableSort(filterFunc.func(records), getComparator(order, orderBy)).slice(
page * rowsPerPage,
(page + 1) * rowsPerPage
);
And I'm guessing records is equivalent to users
, so u can use it like this:
recordsAfterSorting(users).map((item) => (
This should do the trick
Upvotes: 1