user1189352
user1189352

Reputation: 3885

Large editable table: Want to prevent re-rendering of ALL rows upon modification of a table row

Explanation

Because my table is being created / mapped by my data state hook and because my "handleAccountTypeOnChange" function modifies data, that function will always cause my TableRow component to re-render because it is modifying data. so using useCallback wouldn't work in this scenario because of it's dependency on data.

How can I fix this? I can restructure my table state in any way, flatten it more maybe. But I still don't understand how I can solve this without passing in some sort of function that modifies the main data that wouldn't cause all the rows to render. Is the only way to solve this is to use redux? Would love to know how to accomplish this without it.

EDIT

made a codepen. Slightly different because no libraries but same concept. https://codepen.io/daletron3030/pen/PoZqLOx

Code

i get data from my backend and i set it into my data. for the sake of simplicity, my data looks like this:

App Component

const App = () => {
    const [data, setData] = React.useState([
      {
        username: "someUserName1",
        accountType: "admin",
      },
      {
        username: "someUserName2",
        accountType: "normal",
      }, // etc
    ]);

const handleAccountTypeOnChange = (e, {name, value}) => {
  const index = data.findIndex(account => account.username === name);
  setData(update(data, {[index]: {accountType: {$set: value}}}));
};

return (
  <>
    <Table>
      <Table.Body>
        {
          data.map((user, index) => (
            <TableRow
              key={index}
              handleAccountTypeOnChange={handleAccountTypeOnChange}
              username={user.username}
            />
          ))
        }
        </Table.Body>
    </Table>
  </>
)

TableRow Component

const TableRow = React.memo(({ handleAccountTypeOnChange, username }) => {

  const accountTypeOptions = [
    { key: "admin", value: "admin", text: "Administrator" },    
    { key: "normal", value: "normal", text: "Normal" }
  ];

  return (
    <>
      <Table.Row>
        <Table.Cell>
          <Dropdown
            name={username}
            onChange={handleAccountTypeOnChange}
            options={accountTypeOptions}
            selection
            value={accountType}
          />
        </Table.Cell>
        <Table.Cell>{username}</Table.Cell>
      </Table.Row>
    </>
  )});

export default TableRow;

Upvotes: 1

Views: 950

Answers (1)

Ross Allen
Ross Allen

Reputation: 44900

If you use a functional update with your setData call, then the function never needs to change and it can be memoized with useCallback:

const handleAccountTypeOnChange = React.useCallback(
  (e, {name, value}) => {
    setData(prevData => {
      const index = prevData.findIndex(account => account.username === name);
      setData(update(prevData, {[index]: {accountType: {$set: value}}}));
    })
  },
  []
);

This useCallback does not need to list setData as a dependency because the callbacks returned from useState do not change over time.

Upvotes: 1

Related Questions