Search code examples
javascriptreactjstypescriptnextui

React: How to have a column with multiple checkbox selections in NextUI table component


I'm trying to setup several "checkbox" columns in a table using the NextUI table. For each row these columns have a selectable checkbox. I want to be able to make several selections and have those selections "remembered" when moving to different pages in the table, running a search query or removing columns.

These checkboxes are initally selected based off of properties of the row data object. I've setup an example of what I'm trying to achieve here https://codesandbox.io/p/sandbox/eloquent-sun-mshx4t

At the moment there is only one "checkbox" column, but I've done this to make the example as simple as possible and demonstrate the issues I'm facing. I'm getting some unexpected behavior when attempting to select multiple new checkboxes. Every time I click a checkbox it removes the previous selected checkbox if it wasn't part of the initial state.

In the example I provided in the above link there's one checkbox checked in the initial state, if you select try select another two new checkboxes in addition you will see that the final selection will remove the previous selection.

It seems to me the state is not being properly set in the onValuesChange but I can't seem to figure out what I'm doing wrong.

My approach to achieve this was to define const [usersData, setUsersData] = React.useState<User[]>(users) and have the setter called in the Checkbox's onValueChanged like so

       <Checkbox
          isSelected={cellValue as boolean}
          onValueChange={(isSelected) => {
            const newUsers = usersData.map((u) => {
              if (u.id === user.id) {
                return { ...u, checked: isSelected };
              }
              return u;
            });
            setUsersData(newUsers);
          }}
        ></Checkbox>

It's as if the initial state is being "cached" in each checkboxes version of the onValueChange callback.


Solution

  • OK... so I was able to resolve my issue thanks to Dinesh's answer to this SO question: React hooks stale state

    onValueChange = {(isSelected) => {
                    setUsersData((prevState) => {
                      const newUsers = prevState.map((u) =>
                        u.id === user.id ? { ...u, checked: isSelected } : u,
                      );
                      return newUsers;
                    });
                  }}
    

    As demonstrated here: https://codesandbox.io/p/sandbox/musing-tesla-xw2wp9

    From my understanding the state being referenced in my original setState was stale, due to this callback being setup on page render. Using the setter in this manner with (prevState) => {...} enables the setter to pull the latest state.