简体   繁体   中英

Checkbox doesn't respond to onChange when checked is driven by state

I have a material-ui Table and have been implementing multi-select functionality on it.

My multi select should behave as follows:

  • Select checkboxes only appear at the start of a row on hover (when nothing has been selected)
  • Once one row has been selected, all other checkboxes become permanently visible.
  • The checkbox in the TableHead will select / deselect all rows in the Table when toggled.

Each row has its own individual unique id .

I maintain a Set called idsOfSelectedRows via a useState hook to keep track of all rows currently selected.

Each TableRow has a Checkbox which looks like this

<Checkbox
  className={
     idsOfSelectedRows.size === 0 &&
     styles.rowSelectionCheckboxStyle
  }
  onChange={() => handleRowSelectionCheckboxClick}
  checked={idsOfSelectedRows.has(row.id)}
 />

and handleRowSelectionCheckboxClick looks like this

  const handleRowSelectionCheckboxClick = event => {
    const idOfClickedRow = event.target.attributes.getNamedItem("id").value;
   //If the checkbox has just become checked then add it to the idOfSelectedRows Set, otherwise remove it.
    setIdsOfSelectedRows(
      event.target.checked
        ? new Set(idsOfSelectedRows.add(String(idOfClickedRow)))
        : () => {
            idsOfSelectedRows.delete(String(idOfClickedRow));
            return new Set(idsOfSelectedRows);
          }
    );
  };

My issue is that clicking on the checkboxes on the rows is unresponsive. I can select them all by clicking on the select all Checkbox in the TableHead but clicking on checkboxes in individual rows does not change their state?

Here's a full CodeSandbox reproducing the exact issue. What do I need to do to make the checkboxes in the rows be toggleable?

编辑悬停表行复选框

Remove the () => from the onChange assignment for a quick fix.

Why

You're declaring an anonymous inline function, but that function does not call anything (missing () that is syntax to call a function). onChange accepts a function reference. When you give it an inline function, that's the function it will call. The other option is to only pass it the function reference of handleRowSelectionCheckboxClick , and then that function will be called on change instead of the anonymous function middleman.

So your two options are:

onChange={handleRowSelectionCheckboxClick} // Just the reference

OR

onChange={(e) => handleRowSelectionCheckboxClick(e)} // New inline function that calls your function 

The second way is unnecessary in most cases. The first is preferred unless you need to pass custom parameters. Here is an example of how it might be useful:

onChange={(e) => handleRowSelectionCheckboxClick(e, id)}

In your sandbox, you map over elements and each gets a change handler. To treat them separately in the handler, you can pass an extra variable to the function like above.

For example, I got your sandbox working by changing the handler and assignment to this:

const handleRowSelectionCheckboxClick = (event, idOfClickedRow) => {

...

onChange={(e) => handleRowSelectionCheckboxClick(e, row.id)}

This line: event.target.attributes.getNamedItem("id").value; won't give you the required id, since there isn't one passed, change the chekbox like so:

<Checkbox
    id={row.id}
    onChange={handleRowSelectionCheckboxClick}
    checked={idsOfSelectedRows.has(row.id)}
/>

Then, retreive the ID like so:

const idOfClickedRow = event.target.attributes.id.value;

Updated codesandbox :

编辑悬停表行复选框


Like the other answers suggest, change the handleRowSelectionCheckboxClick function call like shown in the example. If you like your solution with an anonymous function, you should add the () : onChange={() => handleRowSelectionCheckboxClick()}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM