Muhammad
Muhammad

Reputation: 21

Updating react component when redux state changes

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

Answers (1)

shaswat.dharaiya
shaswat.dharaiya

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

Related Questions