How to make checkbox work using Chakra UI

Viewed 584

I'm wanting to do a checkbox functionality, where you click on the parent checkbox and it checks all the children. But you can also select an individual checkbox. I'm trying to do this using Chakra UI, but in the documentation they do it using just two checkboxes. In my case, I'm using map to iterate, so the problem that occurs is that I can only check them all, but I can't uncheck an individual checkbox. How would I resolve this?

 import NextLink from 'next/link';
import { useState } from 'react';
import { queryClient } from '../../services/queryClient';
import { api } from '../../services/api';

export default function UserList() {
  const [page, setPage] = useState(1);
  const [checkedItems, setCheckedItems] = useState([false, false]);

  const allChecked = checkedItems.every(Boolean);
  const isIndeterminate = checkedItems.some(Boolean) && !allChecked;

  const { data, isLoading, isFetching, error } = useUsers(page);

  async function handlePrefetchUser(userId: string) {
    await queryClient.prefetchQuery(
      ['user', userId],
      async () => {
        const response = await api.get(`users/${userId}`);

        return response.data;
      },
      {
        staleTime: 1000 * 60 * 10,
      }
    );
  }

  return (
    <Box>
      <Header />

      <Flex my="6" maxWidth={1480} mx="auto" px="6">
        <Sidebar />

        <Box flex="1" borderRadius={8} bg="gray.800" p="8">
          <Flex mb="8" justify="space-between" align="center">
            <Heading size="lg" fontWeight="normal">
              Alunos
              {!isLoading && isFetching && (
                <Spinner size="sm" color="gray.500" ml="4" />
              )}
            </Heading>

            <HStack spacing={4}>
              <NextLink href="/users/create" passHref>
                <Button as="a" size="sm" fontSize="sm" colorScheme="orange">
                  <Icon as={RiAddLine} fontSize="20" />
                </Button>
              </NextLink>

              <NotificationModal />

              <Button
                as="a"
                size="sm"
                fontSize="sm"
                colorScheme="green"
                cursor="pointer"
              >
                <Icon as={CgImport} />
              </Button>
            </HStack>
          </Flex>

          {isLoading ? (
            <Flex justify="center" align="center">
              <Spinner />
            </Flex>
          ) : error ? (
            <Flex justify="center">
              <Text>Falha ao obter dados dos usuários.</Text>
            </Flex>
          ) : (
            <>
              <Table colorScheme="whiteAlpha">
                <Thead>
                  <Tr>
                    <Th px={['4', '4', '6']} color="gray.300" w="8">
                      <Checkbox
                        colorScheme="orange"
                        isChecked={allChecked}
                        isIndeterminate={isIndeterminate}
                        onChange={(e) =>
                          setCheckedItems([e.target.checked, e.target.checked])
                        }
                      />
                    </Th>

                    <Th>Usuários</Th>
                    <Th>Ações</Th>
                    <Th w="8"></Th>
                  </Tr>
                </Thead>

                <Tbody>
                  {data.users.map((user) => (
                    <Tr key={user.id}>
                      <Td px={['4', '4', '6']}>
                        <Checkbox
                          colorScheme="orange"
                          isChecked={checkedItems[0]}
                          onChange={(e) =>
                            setCheckedItems([e.target.checked, checkedItems[1]])
                          }
                        />
                      </Td>

                      <Td>
                        <Box>
                          <Link
                            color="orange.400"
                            onMouseEnter={() => handlePrefetchUser(user.id)}
                          >
                            <Text fontWeight="bold">{user.name}</Text>
                          </Link>
                          <Text fontSize="sm" color="gray.300">
                            {user.email}
                          </Text>
                        </Box>
                      </Td>

                      <Td>
                        <Box cursor="pointer">
                          <HStack spacing={4}>
                            <Text
                              color="orange.400"
                              _hover={{
                                color: 'orange.500',
                              }}
                            >
                              Editar
                            </Text>
                            <Text
                              color="gray.300"
                              _hover={{
                                color: 'gray.500',
                              }}
                            >
                              Excluir
                            </Text>
                          </HStack>
                        </Box>
                      </Td>
                    </Tr>
                  ))}
                </Tbody>
              </Table>

              <Pagination
                totalCountOfRegisters={data.totalCount}
                currentPage={page}
                onPageChange={setPage}
              />
            </>
          )}
        </Box>
      </Flex>
    </Box>
  );
}

1 Answers

Here is a simplified example of how to manage an unknown number of child checkboxes:

https://codesandbox.io/s/stupefied-currying-s3r7dr?file=/src/App.js

For your use case, it might be helpful to also move the checkboxes and checkbox state into a child component that only renders once the users are loaded. That would allow you to set the initial checkbox state with the correct number of users.

Related