Why doesn't my component under my modal window rerender after checkbox selection?

37 Views Asked by At

I am using MantineUI as UI library. When I go to the component for managing users, they are shown as table rows and in the last column of each row there is edit icon which when clicked opens modal window with editing options. I have comboboxes for user type and status of account and that works fine. But under that I have two componenets, the first one is combobox that let's you select topic (ComboBoxTopic) and the second component is list of three checkboxes (PermissionsCheckbox). Now what I want to happen is when I change topic under ComboBoxTopic to update view of PermissionsCheckbox so that it shows permissions of that topic, but what happens is that it only shows permissions of topic that was selected before modal is opened. For example when I select another topic, close modal and reopen it, it shows correct permissions. How can I manage to rerender PermissionsCheckbox when selectedTopic changes?

Main component under which modal window can be opened:

import { Text, Badge, Group, Table, ActionIcon, rem, Flex,} from "@mantine/core";
import { modals } from "@mantine/modals";
import { IconPencil } from "@tabler/icons-react";
import { ComboBox } from "./ComboBox";
import { useState } from "react";
import { UpdateUserRequest } from "../../types/types";
import { ComboBoxTopic } from "./ComboBoxTopic";
import userService from "../../services/user.service";
import { notifications } from "@mantine/notifications";
import { PermissionCheckbox } from "./PermissionsCheckbox";

interface Props {
  user: any;
  topics: any;
  reload: any;
}

const statusColors: Record<string, string> = {
  REQUESTED: "orange",
  APPROVED: "green",
  REJECTED: "red",
};

export function User({ user, topics, reload }: Props) {
  const updatedUser: UpdateUserRequest = JSON.parse(JSON.stringify(user));
  const [selectedTopic, setSelectedTopic] = useState<string>(topics[0].name);

  const openModal = () =>
    modals.openConfirmModal({
      title: `Edit user '${user.username}'`,
      children: (
        <Flex direction="column" align="center" gap="xl">
          <Flex direction="row" align="center" justify="space-evenly" w="100%">
            <Flex direction="column" align="center">
              Role
              <ComboBox
                selected={user.role}
                type="ROLE"
                updatedUser={updatedUser}
              />
            </Flex>
            <Flex direction="column" align="center">
              Status
              <ComboBox
                selected={user.status}
                type="STATUS"
                updatedUser={updatedUser}
              />
            </Flex>
          </Flex>
          <Flex direction="column" align="center" gap="lg">
            <Flex direction="column" align="center">
              Permissions for Topic
              <ComboBoxTopic
                selected={selectedTopic}
                topics={topics}
                updatedUser={updatedUser}
                setSelected={setSelectedTopic}
              />
            </Flex>
            <PermissionCheckbox
              key={selectedTopic}
              selected={selectedTopic}
              user={updatedUser}
              topics={topics}
            />
          </Flex>
        </Flex>
      ),
      centered: true,
      labels: { confirm: "Save", cancel: "Cancel" },
      onConfirm: () => onEditSave(),
    });

  const onEditSave = () => {
    userService
      .updateUser(user.id, updatedUser)
      .then(() => {
        reload();
        notifications.show({
          title: "Update Succeed",
          message: "User was successfully updated",
        });
      })
      .catch(() => {
        notifications.show({
          title: "Update Failed",
          message: "User could not be updated",
        });
      });
  };

  return (
    <Table.Tr key={user.id}>
      <Table.Td>
        <Group gap="sm">
          <Text fz="sm" fw={500}>
            {user.username}
          </Text>
        </Group>
      </Table.Td>
      <Table.Td>
        <Text fz="sm">{user.role}</Text>
      </Table.Td>
      <Table.Td>
        <Badge color={statusColors[user.status]} variant="light">
          {user.status}
        </Badge>
      </Table.Td>
      <Table.Td>
        <Text fz="sm">{user.email}</Text>
      </Table.Td>
      <Table.Td>
        <Group gap={0} justify="flex-end">
          <ActionIcon variant="subtle" color="gray" onClick={openModal}>
            <IconPencil
              style={{ width: rem(24), height: rem(24) }}
              stroke={1.5}
            />
          </ActionIcon>
        </Group>
      </Table.Td>
    </Table.Tr>
  );
}

Checkbox component for selecting topic:

import { useState } from "react";
import { CheckIcon, Combobox, Group, Input, InputBase,} from "@mantine/core";
import { UpdateUserRequest } from "../../types/types";

interface Props {
  selected: string;
  topics?: any;
  updatedUser: UpdateUserRequest;
  setSelected: any;
}

export function ComboBoxTopic({ selected, topics, setSelected }: Props) {
  const combobox = useCombobox({
    onDropdownClose: () => combobox.resetSelectedOption(),
    onDropdownOpen: (eventSource) => {
      if (eventSource === "keyboard") {
        combobox.selectActiveOption();
      } else {
        combobox.updateSelectedOptionIndex("active");
      }
    },
  });

  const [value, setValue] = useState<string>(selected);

  const options = topics.map((topic: any) => (
    <Combobox.Option
      value={topic.name}
      key={topic.name}
      active={topic.name === value}
    >
      <Group gap="xs">
        {topic.name === value && <CheckIcon size={12} />}
        <span>{topic.name}</span>
      </Group>
    </Combobox.Option>
  ));

  return (
    <Combobox
      store={combobox}
      width="40%"
      resetSelectionOnOptionHover
      withinPortal={false}
      onOptionSubmit={(val) => {
        setSelected(val);
        setValue(val);
        combobox.updateSelectedOptionIndex("active");
      }}
    >
      <Combobox.Target targetType="button">
        <InputBase
          component="button"
          type="button"
          pointer
          rightSection={<Combobox.Chevron />}
          rightSectionPointerEvents="none"
          onClick={() => combobox.toggleDropdown()}
        >
          {value || <Input.Placeholder>Pick value</Input.Placeholder>}
        </InputBase>
      </Combobox.Target>

      <Combobox.Dropdown>
        <Combobox.Options>{options}</Combobox.Options>
      </Combobox.Dropdown>
    </Combobox>
  );
}

And the component for showing permissions:

import { Checkbox, Flex } from "@mantine/core";
import { UpdateUserRequest } from "../../types/types";
import { useEffect, useState } from "react";

interface Props {
  selected: string;
  topics: any;
  user: UpdateUserRequest;
}

export function PermissionCheckbox({ selected, topics, user }: Props) {
  const [topicPermissions, setTopicPermissions] = useState<any>();

  useEffect(() => {
    const topic = topics.find((t: any) => t.name === selected);
    setTopicPermissions(
      user.permissions.find((p: any) => p.idTopic === topic.id)
    );
  }, [selected]);

  return (
    <>
      {topicPermissions && (
        <Flex direction="column" gap="md" justify="center" align="flex-start">
          <Checkbox
            label="Write"
            checked={topicPermissions.write}
            onChange={(event) => {
              if (topicPermissions)
                setTopicPermissions((prevTopicPermissions: any) => ({
                  ...prevTopicPermissions,
                  delete: event.currentTarget.checked,
                }));
            }}
          />
          <Checkbox
            label="Update"
            checked={topicPermissions.update}
            onChange={(event) => {
              if (topicPermissions)
                setTopicPermissions((prevTopicPermissions: any) => ({
                  ...prevTopicPermissions,
                  update: event.currentTarget.checked,
                }));
            }}
          />
          <Checkbox
            label="Delete"
            checked={topicPermissions.delete}
            onChange={(event) => {
              if (topicPermissions)
                setTopicPermissions((prevTopicPermissions: any) => ({
                  ...prevTopicPermissions,
                  delete: event.currentTarget.checked,
                }));
            }}
          />
        </Flex>
      )}
    </>
  );
}

I've tried to force update on checkboxes when selected changes but it simply doesn't work. For some reason even the useEffect under PermissionsCheckbox isn't called when selected changes. What could I do to fix this?

0

There are 0 best solutions below