1

I have to do a users/permissions table in which "Permissions" is the X axis and "Users" the Y axis. Rest of the table is populated with checkboxes that indicates if X User has Y Permission. Both of these are dynamic length, and each API has some kind of access to the other one. Ie: Permissions API has for each Permission, an array of Users that have that permission. And viceversa, Users API has for each User, an array of Permissions that the User has.

  1. I am having problem with nested maps and I feel that it's expensive to do it like this? (If there is 40 permissions and 350 users, we talk about 14k checkboxes)

  2. How do I save state for each checkbox? It's ok what I'm trying to do with the checks useState? I also need a way to send updated permissions to another API.

  3. Using the "isChecked" const inside the return statement, I am forcing the checkbox to only be true or false and I can't change that when on click.

  4. At return statement, I should be mapping "checks" but for some reason is not being populated as I am expecting to.

    [{ userID: number, permID: number, checked: boolean }, { userID: number, permID: number, checked: boolean }, etc ]

Code looks something like this:

const [checks, setChecks] = useState([]);

const users = //GET to users API
const permissions = //GET to permissions API

  const mapPermissions = () => {
    users.map((user: any) => {
      permissions.map((permission: any) => {
        setChecks((prevState: any) => [
          ...prevState,
          {
            userID: user.id,
            permID: permission.id,
            //group_lists is an array of Permissions ID that current user has.
            checked: user.group_lists ? user.group_lists.includes(permission.id) : false,
          },
        ]);
      });
    });
  };

  useEffect(() => {
    mapPermissions();
  }, []);

return (
    <TableContainer component={Paper}>
      <Table className={styles.table}>
        <TableHead>
          <TableRow>
            <TableCell className={styles.blank} />
            {permissions.map((permission) => (
              <TableCell key={permission.id} className={styles.header}>
                {permission.name}
              </TableCell>
            ))}
          </TableRow>
        </TableHead>
        <TableBody>

// Should be replacing this for "checks" state map.

              {users.map((user: any) => (
                <TableRow key={user.id}>
                  <TableCell
                    className={styles.left}
                  >{`${user.first_name} ${user.last_name} (${user.username})`}</TableCell>
                {permissions.map((permission: any) => {
                const isChecked = user.group_lists
                  ? user.group_lists.includes(permission.id)
                  : false;

                return (
                  <TableCell key={`${user.id}-${permission.id}`}>
                    <input
                      type="checkbox"
                      checked={isChecked}
                      onChange={(e: ChangeEvent<HTMLInputElement>) => {
                        console.log({
                          user: user.id,
                          permid: permission.id,
                          currentState: isChecked,
                          futureState: e.target.checked,
                        });
                      }}
                    />
                  </TableCell>
                );
              })}
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </TableContainer>
  );

I am open to delete everything if needed. Thanks in advance :)

1 Answer 1

1

i simplified your code snippet a bit to make it easier for me. I would go with something like this

type UpdatesType = {
  userId: number;
  permissionId: number;
  operation: 'add' | 'remove';
}
export const UserPermissionTable: React.FC = ({}) => {
  const [updates, setUpdates] = useState<UpdatesType[]>([]);
  const users =  // get from api
  const permissions =  // get from api

  return (
    <div>
      <table>
        <thead>
          <tr>
            <td />
            {permissions.map((permission) => (
              <td key={permission.id}>{permission.name}</td>
            ))}
          </tr>
        </thead>
        <tbody>
          {users.map((user: any) => (
            <tr key={user.id}>
              <td>{`${user.name}`}</td>
              {permissions.map((permission: any) => {
                const isChecked = user.group_lists
                  ? user.group_lists.includes(permission.id)
                  : false;

                return (
                  <td key={`${user.id}-${permission.id}`}>
                    <input
                      type="checkbox"
                      defaultChecked={isChecked}
                      onChange={(e) => {
                        setUpdates((prev) => [
                          ...prev,
                          {
                            userId: user.id,
                            permissionId: permission.id,
                            operation: e.target.checked ? "add" : "remove",
                          },
                        ]);
                      }}
                    />
                  </td>
                );
              })}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
};

Key points:

  1. you don't need to handle state for each checkbox by yourself, you can use defaultChecked attribute and just update your updates object on checkbox change
  2. in my example i put all updates into an array, you can then send this array with operation wherever you want - you can also remove duplicate operations before sending to API, but this is not an purpose of this question
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.