0

It is a quite common scenario, we have a table with huge data. It has a check box for each row which used to exporting data from selected row.

in App.tsx,

const App = () => {
  const [selection, setSelection] = useState<string[]>([]);

  const handleSelect = useCallback(
    (name: string) => {
      if (selection.includes(name)) {
        selection.splice(selection.indexOf(name), 1);
        setSelection([...selection]);

        return;
      }

      setSelection([...selection, name]);
    },
    [selection, setSelection]
  );

  return (
    <Paper>
      <TableContainer>
        <Table stickyHeader aria-label="sticky table">
          <TableHead>
            ...
          </TableHead>
          <TableBody>
            {rows.map((row) => {
              return (
                <Row
                  key={row.name}
                  row={row}
                  selected={selection.includes(row.name)}
                  onSelect={handleSelect}
                />
              );
            })}
          </TableBody>
        </Table>
      </TableContainer>
    </Paper>
  );
};

In Row.jsx,

export const Row = React.memo(
  ({ selected, row, onSelect }: RowProps) => {
    const handleSelect = () => {
      onSelect(row.name);
    };

    return (
      <TableRow key={row.code}>
        <TableCell key="checkbox">
          <Checkbox checked={selected} onChange={handleSelect} />
        </TableCell>
        // render other columns ...
      </TableRow>
    );
  }
);

Above is the basic code structure. My problem is that when a row is selected/deselected, the app will be re-rendered since the state refreshed, that will cause callback instance refreshed as well, then all child Row will be re-rendered which I want to avoid because of performance issue. useCallback does not work for this case since it has dependency on the selection state.

I know I can implement shouldComponentUpdate in child component, just wonder if possible to fix the issue from root ?

Appreciate for any suggestions and happy new year.

UPDATE: Turn parent into React.Component instead of function component can solve the issue because setState will not refresh callback instance. I thought those two kinds of components are pretty much same, but apparently they are different. Hope it could help someone, but still wonder if possible to archive this in function component.

smilingky
  • 379
  • 3
  • 4

1 Answers1

0

You can do these multiple ways, Here I mentioned a couple.

  1. You can pass arePropsEqual function with your custom implementation to React.memo function in the child component.
  2. Currently you are passing an object(row) to the child component. instead of passing the object pass each individual props separately. The component does a shallow comparison before rendering the component. if you pass an object shallow comparison always returns props are not equal, that's why it is re-rendering.
Sam
  • 1,040
  • 1
  • 7
  • 11
  • Thanks for reply, but as I described I am looking for other solution besides shouldComponentUpdate, arePropsEqual is the same thing. The reason caused child component is not the row data, I can confirm row data not changed, it is the onSelect callback which instance will be regenerated after the selection state changed in App.tsx (the parent component). – smilingky Jan 05 '21 at 15:25